我使用基于this描述中的按需加载演示的MVVM模式在C#中实现了TreeView。之后我尝试添加一个函数,不仅可以选择,还可以在TreeView中聚焦元素。因此我实现了这个类:
namespace TreeViewWithViewModelDemo.LoadOnDemand
{
public static class FocusExtension{
public static bool GetIsFocused(DependencyObject obj){
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value){
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached(
"IsFocused", typeof(bool), typeof(FocusExtension),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e){
var uie = (UIElement)d;
if ((bool)e.NewValue) {
uie.Focus(); // Don't care about false values.
}
}
}
}
在我的TreeViewItemViewModel.cs中,我添加了参数IsTextBoxFocused:
private bool _isFocused = true;
public bool IsTextBoxFocused
{
get{ return _isFocused; }
set
{
PropertyChangedEventHandler handler = PropertyChanged;
if (_isFocused == value)
{
_isFocused = false;
if (handler != null)
handler(this, new PropertyChangedEventArgs("IsTextBoxFocused"));
}
_isFocused = value;
if (handler != null)
handler(this, new PropertyChangedEventArgs("IsTextBoxFocused"));
}
}
在我的XAML中,我更改了区域的HierarchicalDataTemplate:
<HierarchicalDataTemplate
DataType="{x:Type local:RegionViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<Grid local:FocusExtension.IsFocused="{Binding IsTextBoxFocused}" Focusable="True">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename"/>
</ContextMenu>
</Grid.ContextMenu>
<!-- Normal state of the header -->
<TextBlock x:Name="textBlockHeader" Focusable="True" Text="{Binding RegionName}" Margin="3,0" />
<!-- This state is active in the edit mode -->
</Grid>
</StackPanel>
</HierarchicalDataTemplate>
直到这里,我的代码完全正常,但仅适用于我的树中的Region节点。在下一步中,我试图让State节点也成为焦点。因此,我使用与IsTextBoxFocused相同的方式实现了一个新变量IsGridBoxFocused。在我的XAML中,我更改了状态的HierarchicalDataTemplate:
<HierarchicalDataTemplate
DataType="{x:Type local:StateViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<Grid local:FocusExtension.IsFocused="{Binding IsGridBoxFocused}" Focusable="True">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename"/>
</ContextMenu>
</Grid.ContextMenu>
<!-- Normal state of the header -->
<TextBlock x:Name="textBlockHeader" Focusable="True" Text="{Binding StateName}" Margin="3,0" />
<!-- This state is active in the edit mode -->
</Grid>
</StackPanel>
</HierarchicalDataTemplate>
最后我更改了IsSelected功能。 在TreeViewItemViewModel中:
public virtual bool IsSelected
{
get { return _isSelected; }
set
{
if (value != _isSelected)
{
_isSelected = value;
this.OnPropertyChanged("IsSelected");
if (!_isSelected)
{
this.IsExpanded = false;
}
else
{
this.IsExpanded = true;
}
}
}
}
在RegionViewModel中:
public override bool IsSelected
{
get
{
return base.IsSelected;
}
set
{
base.IsSelected = value;
IsTextBoxFocused = value;
if (value == true)
{
foreach (var elem in this.Children)
{
elem.IsGridBoxFocused = !value;
}
}
}
}
在StateViewModel中:
public override bool IsSelected
{
get
{
return base.IsSelected;
}
set
{
IsGridBoxFocused = value;
Parent.IsTextBoxFocused = !value;
base.IsSelected = value;
}
}
我的问题是,当我选择一个Region节点时它可以正常工作,当我第一次选择State(Child)节点时它也可以工作,但是当我选择第二个State节点时,会选择一个父节点。有人可以帮我解决这个问题吗?