Ael*_*anY 8 c# wpf .net-4.0 mvvm treeviewitem
我有一个MVVM应用程序,在窗口的左侧有一个WPF TreeView.右侧的详细信息面板根据所选的树节点更改内容.
如果用户选择节点,则详细信息面板的内容会立即更改.如果用户点击该节点,则需要这样做,但如果用户使用键向下/向上导航树,我想延迟更改内容.(与Windows资源管理器的行为相同,至少在Win XP下)我假设我必须在我的ViewModel中知道是否通过鼠标或键盘选择了节点.
我怎样才能做到这一点?
更新:
这是我的第一篇文章,因此我不确定这是不是正确的地方,但我想让社区知道我在此期间做了什么.这是我自己的解决方案.我不是专家,所以我不知道这是不是一个好的解决方案.但它适用于我,如果能帮助别人,我会很高兴.错误修正,改进或更好的解决方案非常受欢迎.
我创建了下面附加的属性HasMouseFocus ...
(首先我使用了MouseEnterEvent但是如果用户使用键向上/向下导航树并且鼠标指针随机遍布任何导航的树项目,这不能很好地工作,因为在这种情况下详细信息立即更新.)
public static bool GetHasMouseFocus(TreeViewItem treeViewItem)
{
return (bool)treeViewItem.GetValue(HasMouseFocusProperty);
}
public static void SetHasMouseFocus(TreeViewItem treeViewItem, bool value)
{
treeViewItem.SetValue(HasMouseFocusProperty, value);
}
public static readonly DependencyProperty HasMouseFocusProperty =
DependencyProperty.RegisterAttached(
"HasMouseFocus",
typeof(bool),
typeof(TreeViewItemProperties),
new UIPropertyMetadata(false, OnHasMouseFocusChanged)
);
static void OnHasMouseFocusChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
TreeViewItem item = depObj as TreeViewItem;
if (item == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
{
item.MouseDown += OnMouseDown;
item.MouseLeave += OnMouseLeave;
}
else
{
item.MouseDown -= OnMouseDown;
item.MouseLeave -= OnMouseLeave;
}
}
/// <summary>
/// Set HasMouseFocusProperty on model of associated element.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnMouseDown(object sender, MouseEventArgs e)
{
if (sender != e.OriginalSource)
return;
TreeViewItem item = sender as TreeViewItem;
if ((item != null) & (item.HasHeader))
{
// get the underlying model of current tree item
TreeItemViewModel header = item.Header as TreeItemViewModel;
if (header != null)
{
header.HasMouseFocus = true;
}
}
}
/// <summary>
/// Clear HasMouseFocusProperty on model of associated element.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnMouseLeave(object sender, MouseEventArgs e)
{
if (sender != e.OriginalSource)
return;
TreeViewItem item = sender as TreeViewItem;
if ((item != null) & (item.HasHeader))
{
// get the underlying model of current tree item
TreeItemViewModel header = item.Header as TreeItemViewModel;
if (header != null)
{
header.HasMouseFocus = false;
}
}
}
Run Code Online (Sandbox Code Playgroud)
...并将其应用于TreeView.ItemContainerStyle
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" >
<!-- These Setters binds some properties of a TreeViewItem to the TreeViewItemViewModel. -->
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="ToolTip" Value="{Binding Path=CognosBaseClass.ToolTip}"/>
<!-- These Setters applies attached behaviors to all TreeViewItems. -->
<Setter Property="properties:TreeViewItemProperties.PreviewMouseRightButtonDown" Value="True" />
<Setter Property="properties:TreeViewItemProperties.BringIntoViewWhenSelected" Value="True" />
<Setter Property="properties:TreeViewItemProperties.HasMouseFocus" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
Run Code Online (Sandbox Code Playgroud)
凡性质是我的附加属性的路径.
xmlns:properties="clr-namespace:WPF.MVVM.AttachedProperties;assembly=WPF.MVVM"
Run Code Online (Sandbox Code Playgroud)
然后在我的ViewModel中,如果HasMousefocusProperty为true,我立即更新详细信息面板(GridView).如果为false,我只需启动DispatcherTimer并将当前选定的项目应用为Tag.间隔为500毫秒后,Tick-Event会应用详细信息,但前提是所选项目仍然与Tag相同.
/// <summary>
/// This property is beeing set when the selected item of the tree has changed.
/// </summary>
public TreeItemViewModel SelectedTreeItem
{
get { return Property(() => SelectedTreeItem); }
set
{
Property(() => SelectedTreeItem, value);
if (this.SelectedTreeItem.HasMouseFocus)
{
// show details for selected node immediately
ShowGridItems(value);
}
else
{
// delay showing details
this._selctedNodeChangedTimer.Stop();
this._selctedNodeChangedTimer.Tag = value;
this._selctedNodeChangedTimer.Start();
}
}
}
Run Code Online (Sandbox Code Playgroud)
OnPreviewKeyDown您可以为您的TreeView(或拥有它的用户控件)处理并以编程方式在 ViewModel 中设置一个标志,并在刷新详细信息面板时考虑它 -
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
{
switch(e.Key)
{
case Key.Up:
case Key.Down:
MyViewModel.IsUserNavigating = true;
break;
}
}
Run Code Online (Sandbox Code Playgroud)
这个SO问题中提到了类似的方法和其他解决方案 -
如何以编程方式导航(不是选择,而是导航)WPF TreeView?
更新: [回应 AalanY 的评论]
我认为在视图中保留一些代码隐藏不会有任何问题,这不会破坏 MVVM。
在文章《WPF Apps With The Model-View-ViewModel Design Pattern》中,作者 Josh Smith 说道:
在设计良好的 MVVM 架构中,大多数视图的代码隐藏应该是空的,或者最多只包含操作该视图中包含的控件和资源的代码。有时,还需要在 View 的代码隐藏中编写与 ViewModel 对象交互的代码,例如挂钩事件或调用 很难从 ViewModel 本身调用的方法。
根据我的经验,在没有任何代码隐藏的情况下构建企业(相当大的规模)应用程序是不可能的,特别是当您必须使用复杂的控件(如 TreeView、DataGrid 或第三方控件)时。
| 归档时间: |
|
| 查看次数: |
620 次 |
| 最近记录: |