问题
Does anyone know how to change the IsReadOnly property of controls (TextBox, ComboBox, etc.) in a WPF HierarchicalDataTemplate dynamically?
I want to be able to make the controls contained in the HierarchicalDataTemplate editable for some users and read-only for others.
I have tried binding the IsReadOnly property on each control in the HierarchicalDataTemplate to a predetermined Boolean value in the page's ViewModel, but am unable to get the binding to work. Any help is greatly appreciated.
VIEWMODEL:
private bool _isReadOnlyBool;
public bool isReadOnlyBool
{
get { return _isReadOnlyBool; }
set
{
_isReadOnlyBool = value;
RaiseChange("isReadOnlyBool");
}
}
Here I show a TreeView control containing a HierarchicalDataTemplate. Notice that I attempt to Bind to the IsReadOnly value of the TextBox in the HierarchicalDataTemplate, to the Boolean "isReadOnlyBool" value from the page's ViewModel.
VIEW:
<TreeView HorizontalAlignment="Center" x:Name="treeView1" VerticalAlignment="Top" ItemsSource="{Binding Path=rsParentChild}" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" >
<TreeView.ItemContainerStyle>
<Style>
<Setter Property="TreeViewItem.IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=rsParentChild, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<Grid Focusable="False" Margin="5,10,5,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Content="Action Text" FontSize="8" Grid.Row="0" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="0"
IsReadOnly="{Binding isReadOnlyBool, RelativeSource={RelativeSource AncestorType={x:Type Page}}}"
Background="#99FFFFFF"
BorderBrush="Black"
Text="{Binding Path=actionText, Mode=TwoWay}"
TextWrapping="Wrap" Margin="0,0,0,0"
LostFocus="TextBox_LostFocus"
/>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
I get the following binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'isReadOnlyBool' property not found on 'object' ''actions' (Name='')'. BindingExpression:Path=isReadOnlyBool; DataItem='actions' (Name=''); target element is 'TextBox' (Name=''); target property is 'IsReadOnly' (type 'Boolean')
回答1:
How is your model/viewmodel structured? I had a problem similar to this, but I was just using textblocks instead, but some needed to be bold, have different background colors, etc, so binding those dynamically was necessary. I modified my code to be similar to what you would need, adding TextBoxes instead of TextBlocks, and I was able to get the IsReadOnly property working. This is the XAML for my TreeView looks like now that it's modified.
<TreeView x:Name="Tree" ItemsSource="{Binding Account}" Margin="-2,45,-4,-18" BorderThickness="0,0,3,0" BorderBrush="{x:Null}" MouseDoubleClick="TreeViewItem_MouseDoubleClick_1">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Account}" DataType="{x:Type local2:Accounts}">
<TextBox Text="{Binding Header}" IsReadOnly="{Binding IsReadOnly}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
An this is what my Model looks like.
public class Accounts
{
private readonly List<Accounts> accounts;
public Accounts()
{
accounts = new List<Accounts>();
}
public bool IsNodeExpanded { get; set; }
public string Header { get; set; }
public Brush Foreground { get; set; }
public Brush Background { get; set; }
public FontWeight FontWeight { get; set; }
public string Parent { get; set; }
public bool IsReadOnly { get; set; }
public List<Accounts> Account
{
get { return accounts; }
}
}
You can see that I have properties added as necessary, for my purpose I needed everything except IsReadOnly. I added that in for the TextBox. I used the Accounts list to create a tree like structure in my ViewModel, and that is what is bound to my ItemsSource. I'll spare you the code from my ViewModel because it's pretty ugly, but I'll post a small sample of something that would work.
private List<Accounts> accounts;
public List<Accounts> Account
{
get { return accounts; }
set
{
accounts = value;
NotifyPropertyChanged("Accounts");
}
}
void SetTree()
{
Account.Add(new Accounts { Header = "Accounts", IsReadOnly = true });
Account[0].Account.Add(new Accounts { Header = "Product Type", Foreground = fGround, FontWeight = FontWeights.Bold, IsReadOnly = true });
SortedSet<string> uniqueProductType = GetUniqueItems("Product Type");
Accounts tempAccount;
for (int i = 0; i < uniqueProductType.Count(); i++)
{
tempAccount = new Accounts { Header = uniqueProductType.ElementAt(i), Foreground = fGround, FontWeight = FontWeights.Normal };
accountsSystemNode.Add(uniqueProductType.ElementAt(i), tempAccount);
tempAccount.Account.Add(new Accounts { Header = "Client Preferred Account Name", Foreground = fGround, FontWeight = FontWeights.Bold, IsReadOnly = true });
Account[0].Account[0].Account.Add(tempAccount);
}
}
To give some context to this code, my Tree starts with a title, “Accounts”, and then gives a group of subcategories. One of these subcategories is “Product Type”. Account[0] is “Accounts”, and a node of “Account” is Account[0][0], “Product Type”. Then I populate product type by cycling through the list of product types I have, create a new Account object and set the necessary values, and add that to my “Product Type” node. Notice I don’t have the IsReadOnly value set for these. This is how I verified it works. For each Subcategory title I set the IsReadOnly property to true and could not edit them, while the actual values within this subcategory, IsReadOnly is false, and I was able to edit those values.
This is what this tree would look like. I was able to edit those text boxes as you can see, but I wasn't able to edit "Accounts" or "Product Type".
来源:https://stackoverflow.com/questions/27928476/how-do-i-dynamically-make-user-controls-in-a-hierarchicaldatatemplate-editable-r