У меня есть приложение 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;
}
}
}
. ..и применил его к 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>
Где properties— это путь к моему прикрепленному свойству.
xmlns:properties="clr-namespace:WPF.MVVM.AttachedProperties;assembly=WPF.MVVM"
Затем в моей ViewModel, если HasMousefocusPropertyравно true, я немедленно обновляю панель сведений (GridView). Если false, я просто запускаю DispatcherTimer и применяю текущий выбранный элемент в качестве тега. После интервала в 500 мс Tick-Event применяет детали, но только если выбранный элемент все еще совпадает с тегом.
/// <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();
}
}
}