我解决了一个树形视图和一次只显示一个文档的问题。这个解决方案基于一个可附加的行为,可以附加到普通的树形视图上:
<TreeView Grid.Column="0"
ItemsSource=""
behav:TreeViewSelectionChangedBehavior.ChangedCommand=""
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="">
<StackPanel Orientation="Horizontal">
<TextBlock Text=""
ToolTipService.ShowOnDisabled="True"
VerticalAlignment="Center" Margin="3" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="">
<Setter Property="IsExpanded" Value="" />
<Setter Property="IsSelected" Value="" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
行为的代码如下:
public static class TreeViewSelectionChangedBehavior
{
#region fields
private static readonly DependencyProperty ChangedCommandProperty = DependencyProperty.RegisterAttached(
"ChangedCommand",
typeof(ICommand),
typeof(TreeViewSelectionChangedBehavior),
new PropertyMetadata(null, OnSelectionChangedCommandChange));
public static readonly DependencyProperty UndoSelectionProperty =
DependencyProperty.RegisterAttached("UndoSelection",
typeof(bool),
typeof(TreeViewSelectionChangedBehavior),
new PropertyMetadata(false, OnUndoSelectionChanged));
#endregion fields
#region methods
#region ICommand changed methods
public static void SetChangedCommand(DependencyObject source, ICommand value)
{
source.SetValue(ChangedCommandProperty, value);
}
public static ICommand GetChangedCommand(DependencyObject source)
{
return (ICommand)source.GetValue(ChangedCommandProperty);
}
#endregion ICommand changed methods
#region UndoSelection methods
public static bool GetUndoSelection(DependencyObject obj)
{
return (bool)obj.GetValue(UndoSelectionProperty);
}
public static void SetUndoSelection(DependencyObject obj, bool value)
{
obj.SetValue(UndoSelectionProperty, value);
}
#endregion UndoSelection methods
private static void OnSelectionChangedCommandChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TreeView uiElement = d as TreeView;
if (uiElement != null)
{
uiElement.SelectedItemChanged -= Selection_Changed;
var command = e.NewValue as ICommand;
if (command != null)
{
uiElement.SelectedItemChanged += Selection_Changed;
}
}
}
private static void Selection_Changed(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var uiElement = sender as TreeView;
if (uiElement == null)
return;
ICommand changedCommand = TreeViewSelectionChangedBehavior.GetChangedCommand(uiElement);
if (changedCommand == null)
return;
if (changedCommand is RoutedCommand)
{
(changedCommand as RoutedCommand).Execute(e.NewValue, uiElement);
}
else
{
changedCommand.Execute(e.NewValue);
}
}
private static void OnUndoSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TreeView uiElement = d as TreeView;
if (uiElement != null)
{
uiElement.PreviewMouseDown -= uiElement_PreviewMouseDown;
var command = (bool)e.NewValue;
if (command == true)
{
uiElement.PreviewMouseDown += uiElement_PreviewMouseDown;
}
}
}
private static void uiElement_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var source = e.OriginalSource as DependencyObject;
while (source != null && !(source is TreeViewItem))
source = VisualTreeHelper.GetParent(source);
var itemSource = source as TreeViewItem;
if (itemSource == null)
return;
var treeView = sender as TreeView;
if (treeView == null)
return;
bool undoSelection = TreeViewSelectionChangedBehavior.GetUndoSelection(treeView);
if (undoSelection == false)
return;
var result = MessageBox.Show("The current document has unsaved data. Do you want to continue without saving data?", "Are you really sure?",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (result == MessageBoxResult.No)
{
e.Handled = true;
}
else
{
treeView.PreviewMouseDown -= uiElement_PreviewMouseDown;
if (itemSource.IsSelected == true )
itemSource.IsSelected = false;
itemSource.IsSelected = true;
treeView.PreviewMouseDown += uiElement_PreviewMouseDown;
}
}
#endregion methods
}
在这个例子中,我展示了一个阻塞消息框,以便在PreviewMouseDown事件发生时阻止它。然后处理该事件以表示选择被取消或者如果未处理,则让treeview本身通过选择要选择的项来处理该事件。
如果用户决定继续(PreviewMouseDown事件未由附加行为处理并调用绑定命令),则该行为会在viewmodel中调用绑定命令。
我想显示消息框可能可以用其他方式完成,但我认为在此处阻止事件发生是至关重要的,因为否则无法取消它(?)。因此,我唯一能想到改进这段代码的方法就是绑定一些字符串使得显示的消息可配置。
我写了一篇文章,其中包含可下载的示例,因为这是一个难以解释的领域(必须对缺失部分做出很多假设,而这些假设可能不会被所有读者共享)。
这里有一篇包含我的结果的文章:
http://www.codeproject.com/Articles/995629/Cancelable-TreeView-Navigation-for-Documents-in-WP
请评论这个解决方案,并让我知道是否有改进的空间。