14

Treeview コントロールを備えた WPF アプリケーションがあります。

ユーザーがツリー上のノードをクリックすると、ページ上の他の TextBox、ComboBox などのコントロールに適切な値が設定されます。

その後、ユーザーはこれらの値を変更し、[保存] ボタンをクリックして変更を保存できます。

ただし、ユーザーが変更を保存せずに別の Treeview ノードを選択した場合、警告とその選択をキャンセルする機会を表示したいと考えています。

MessageBox: 続行して未保存の変更を破棄しますか? OK/キャンセル http://img522.imageshack.us/img522/2897/discardsj3.gif

XAML...

<TreeView Name="TreeViewThings"
    ...
    TreeViewItem.Unselected="TreeViewThings_Unselected"
    TreeViewItem.Selected="TreeViewThings_Selected" >

ビジュアルベーシック...

Sub TreeViewThings_Unselected (ByVal 送信者 As System.Object, _
                              ByVal e As System.Windows.RoutedEventArgs)
    Dim OldThing As Thing = DirectCast(e.OriginalSource.DataContext, Thing)
    If CancelDueToUnsavedChanges(OldThing) Then
        'ここにキャンセルコードを入れる
    終了条件
サブ終了

Sub TreeViewThings_Selected(ByVal 送信者 As System.Object, _
                            ByVal e As System.Windows.RoutedEventArgs)
    Dim NewThing As Thing = DirectCast(e.OriginalSource.DataContext, Thing)
    PopulateControlsFromThing(NewThing)
サブ終了

これらの選択解除/選択イベントをキャンセルするにはどうすればよいですか?


更新: フォローアップの質問をしました...
MessageBox 確認で PreviewMouseDown イベントを適切に処理するにはどうすればよいですか?

4

9 に答える 9

12

アップデート

代わりに SelectedItemChanged にロジックを配置できることに気付きました。少しきれいな解決策。

Xaml

<TreeView Name="c_treeView"
          SelectedItemChanged="c_treeView_SelectedItemChanged">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

コードビハインド。TreeView の ItemsSource であるクラスがいくつかあるので、それらすべての IsSelected プロパティを公開するインターフェイス (MyInterface) を作成しました。

private MyInterface m_selectedTreeViewItem = null;
private void c_treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    if (m_selectedTreeViewItem != null)
    {
        if (e.NewValue == m_selectedTreeViewItem)
        {
            // Will only end up here when reversing item
            // Without this line childs can't be selected
            // twice if "No" was pressed in the question..   
            c_treeView.Focus();   
        }
        else
        {
            if (MessageBox.Show("Change TreeViewItem?",
                                "Really change",
                                MessageBoxButton.YesNo,
                                MessageBoxImage.Question) != MessageBoxResult.Yes)
            {
                EventHandler eventHandler = null;
                eventHandler = new EventHandler(delegate
                {
                    c_treeView.LayoutUpdated -= eventHandler;
                    m_selectedTreeViewItem.IsSelected = true;
                });
                // Will be fired after SelectedItemChanged, to early to change back here
                c_treeView.LayoutUpdated += eventHandler;
            }   
            else
            {
                m_selectedTreeViewItem = e.NewValue as MyInterface;
            }        
        }
    }
    else
    {
        m_selectedTreeViewItem = e.NewValue as MyInterface;
    }
}

「いいえ」を押しても前の項目に戻らないという状況は見当たりませんでした。

于 2010-10-27T00:22:42.237 に答える
4

同じ問題を解決する必要がありましたが、アプリケーションの複数のツリービューで解決しました。部分的に Meleak のソリューションを使用し、部分的に次のフォーラムの拡張メソッドを使用して、TreeView を派生させ、イベント ハンドラーを追加しました: http://forums.silverlight.net/t/65277.aspx/1/10

私のソリューションをあなたと共有したいと思ったので、「ノード変更のキャンセル」を処理する完全な再利用可能な TreeView を次に示します。

public class MyTreeView : TreeView
{
    public static RoutedEvent PreviewSelectedItemChangedEvent;
    public static RoutedEvent SelectionCancelledEvent;

    static MyTreeView()
    {
        PreviewSelectedItemChangedEvent = EventManager.RegisterRoutedEvent("PreviewSelectedItemChanged", RoutingStrategy.Bubble,
                                                                           typeof(RoutedPropertyChangedEventHandler<object>), typeof(MyTreeView));

        SelectionCancelledEvent = EventManager.RegisterRoutedEvent("SelectionCancelled", RoutingStrategy.Bubble,
                                                                   typeof(RoutedEventHandler), typeof(MyTreeView));
    }

    public event RoutedPropertyChangedEventHandler<object> PreviewSelectedItemChanged
    {
        add { AddHandler(PreviewSelectedItemChangedEvent, value); }
        remove { RemoveHandler(PreviewSelectedItemChangedEvent, value); }
    }

    public event RoutedEventHandler SelectionCancelled
    {
        add { AddHandler(SelectionCancelledEvent, value); }
        remove { RemoveHandler(SelectionCancelledEvent, value); }
    }


    private object selectedItem = null;
    protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
    {
        if (e.NewValue == selectedItem)
        {
            this.Focus();

            var args = new RoutedEventArgs(SelectionCancelledEvent);
            RaiseEvent(args);
        }
        else
        {
            var args = new RoutedPropertyChangedEventArgs<object>(e.OldValue, e.NewValue, PreviewSelectedItemChangedEvent);
            RaiseEvent(args);

            if (args.Handled)
            {
                EventHandler eventHandler = null;
                eventHandler = delegate
                {
                    this.LayoutUpdated -= eventHandler;

                    var treeViewItem = this.ContainerFromItem(selectedItem);
                    if (treeViewItem != null)
                        treeViewItem.IsSelected = true;
                };

                this.LayoutUpdated += eventHandler;
            }
            else
            {
                selectedItem = this.SelectedItem;
                base.OnSelectedItemChanged(e);
            }
        }
    }
}

public static class TreeViewExtensions
{
    public static TreeViewItem ContainerFromItem(this TreeView treeView, object item)
    {
        if (item == null) return null;

        var containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item);

        return containerThatMightContainItem ?? ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item);
    }

    private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)
    {
        foreach (var child in itemCollection)
        {
            var parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(child);
            var containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);

            if (containerThatMightContainItem != null)
                return containerThatMightContainItem;

            var recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);
            if (recursionResult != null)
                return recursionResult;
        }
        return null;
    }
}

使用例を次に示します (MyTreeView を含むウィンドウの分離コード):

    private void theTreeView_PreviewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        if (e.OldValue != null)
            e.Handled = true;
    }

    private void theTreeView_SelectionCancelled(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Cancelled");
    }

ツリービューで最初のノードを選択すると、他のすべてのノードの変更がキャンセルされ、メッセージ ボックスが表示されます。

于 2012-03-22T07:11:43.113 に答える
3

ロジックが存在する場合、選択されたアイテムが実際に既に変更されている場合、ロジックを OnSelectedItemChanged メソッドに実際に配置することはできません。

別の投稿者が示唆しているように、PreviewMouseDown ハンドラーはロジックを実装するのに適した場所ですが、まだかなりの量の脚の作業を行う必要があります。

以下は私の2セントです:

最初に私が実装した TreeView:

public class MyTreeView : TreeView
{
    static MyTreeView( )
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(MyTreeView),
            new FrameworkPropertyMetadata(typeof(TreeView)));
    }

    // Register a routed event, note this event uses RoutingStrategy.Tunnel. per msdn docs
    // all "Preview" events should use tunneling.
    // http://msdn.microsoft.com/en-us/library/system.windows.routedevent.routingstrategy.aspx
    public static RoutedEvent PreviewSelectedItemChangedEvent = EventManager.RegisterRoutedEvent(
        "PreviewSelectedItemChanged",
        RoutingStrategy.Tunnel,
        typeof(CancelEventHandler),
        typeof(MyTreeView));

    // give CLR access to routed event
    public event CancelEventHandler PreviewSelectedItemChanged
    {
        add
        {
            AddHandler(PreviewSelectedItemChangedEvent, value);
        }
        remove
        {
            RemoveHandler(PreviewSelectedItemChangedEvent, value);
        }
    }

    // override PreviewMouseDown
    protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
    {
        // determine which item is going to be selected based on the current mouse position
        object itemToBeSelected = this.GetObjectAtPoint<TreeViewItem>(e.GetPosition(this));

        // selection doesn't change if the target point is null (beyond the end of the list)
        // or if the item to be selected is already selected.
        if (itemToBeSelected != null && itemToBeSelected != SelectedItem)
        {
            bool shouldCancel;

            // call our new event
            OnPreviewSelectedItemChanged(out shouldCancel);
            if (shouldCancel)
            {
                // if we are canceling the selection, mark this event has handled and don't
                // propogate the event.
                e.Handled = true;
                return;
            }
        }

        // otherwise we want to continue normally
        base.OnPreviewMouseDown(e);
    }

    protected virtual void OnPreviewSelectedItemChanged(out bool shouldCancel)
    {
        CancelEventArgs e = new CancelEventArgs( );
        if (PreviewSelectedItemChangedEvent != null)
        {
            // Raise our event with our custom CancelRoutedEventArgs
            RaiseEvent(new CancelRoutedEventArgs(PreviewSelectedItemChangedEvent, e));
        }
        shouldCancel = e.Cancel;
    }
}

マウスの下にあるオブジェクトを検索する TreeView をサポートするいくつかの拡張メソッド。

public static class ItemContainerExtensions
{
    // get the object that exists in the container at the specified point.
    public static object GetObjectAtPoint<ItemContainer>(this ItemsControl control, Point p)
        where ItemContainer : DependencyObject
    {
        // ItemContainer - can be ListViewItem, or TreeViewItem and so on(depends on control)
        ItemContainer obj = GetContainerAtPoint<ItemContainer>(control, p);
        if (obj == null)
            return null;

        // it is worth noting that the passed _control_ may not be the direct parent of the
        // container that exists at this point. This can be the case in a TreeView, where the
        // parent of a TreeViewItem may be either the TreeView or a intermediate TreeViewItem
        ItemsControl parentGenerator = obj.GetParentItemsControl( );

        // hopefully this isn't possible?
        if (parentGenerator == null)
            return null;

        return parentGenerator.ItemContainerGenerator.ItemFromContainer(obj);
    }

    // use the VisualTreeHelper to find the container at the specified point.
    public static ItemContainer GetContainerAtPoint<ItemContainer>(this ItemsControl control, Point p)
        where ItemContainer : DependencyObject
    {
        HitTestResult result = VisualTreeHelper.HitTest(control, p);
        DependencyObject obj = result.VisualHit;

        while (VisualTreeHelper.GetParent(obj) != null && !(obj is ItemContainer))
        {
            obj = VisualTreeHelper.GetParent(obj);
        }

        // Will return null if not found
        return obj as ItemContainer;
    }

    // walk up the visual tree looking for the nearest ItemsControl parent of the specified
    // depObject, returns null if one isn't found.
    public static ItemsControl GetParentItemsControl(this DependencyObject depObject)
    {
        DependencyObject obj = VisualTreeHelper.GetParent(depObject);
        while (VisualTreeHelper.GetParent(obj) != null && !(obj is ItemsControl))
        {
            obj = VisualTreeHelper.GetParent(obj);
        }

        // will return null if not found
        return obj as ItemsControl;
    }
}

最後になりましたが、RoutedEvent サブシステムを利用するカスタム EventArgs です。

public class CancelRoutedEventArgs : RoutedEventArgs
{
    private readonly CancelEventArgs _CancelArgs;

    public CancelRoutedEventArgs(RoutedEvent @event, CancelEventArgs cancelArgs)
        : base(@event)
    {
        _CancelArgs = cancelArgs;
    }

    // override the InvokeEventHandler because we are going to pass it CancelEventArgs
    // not the normal RoutedEventArgs
    protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget)
    {
        CancelEventHandler handler = (CancelEventHandler)genericHandler;
        handler(genericTarget, _CancelArgs);
    }

    // the result
    public bool Cancel
    {
        get
        {
            return _CancelArgs.Cancel;
        }
    }
}
于 2013-03-19T15:21:58.300 に答える
2

CAMS_ARIES:

XAML:

コード:

  private bool ManejarSeleccionNodoArbol(Object origen)
    {
        return true;  // with true, the selected nodo don't change
        return false // with false, the selected nodo change
    }


    private void Arbol_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {            
        if (e.Source is TreeViewItem)
        {
            e.Handled = ManejarSeleccionNodoArbol(e.Source);
        }
    }

    private void Arbol_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Source is TreeViewItem)
        {
           e.Handled=ManejarSeleccionNodoArbol(e.Source);
        }
    }         
于 2009-08-14T02:03:20.443 に答える
2

クロージング イベントなどのように、イベントをキャンセルすることはできません。ただし、最後に選択した値をキャッシュすると元に戻すことができます。秘訣は、SelectionChanged イベントを再起動せずに選択を変更する必要があることです。次に例を示します。

    private object _LastSelection = null;
    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (IsUpdated)
        {
            MessageBoxResult result = MessageBox.Show("The current record has been modified. Are you sure you want to navigate away? Click Cancel to continue editing. If you click OK all changes will be lost.", "Warning", MessageBoxButton.OKCancel, MessageBoxImage.Hand);
            switch (result)
            {
                case MessageBoxResult.Cancel:
                    e.Handled = true;
                    // disable event so this doesn't go into an infinite loop when the selection is changed to the cached value
                    PersonListView.SelectionChanged -= new SelectionChangedEventHandler(OnSelectionChanged);
                    PersonListView.SelectedItem = _LastSelection;
                    PersonListView.SelectionChanged += new SelectionChangedEventHandler(OnSelectionChanged);
                    return;
                case MessageBoxResult.OK:
                    // revert the object to the original state
                    LocalDataContext.Persons.GetOriginalEntityState(_LastSelection).CopyTo(_LastSelection);
                    IsUpdated = false;
                    Refresh();
                    break;
                default:
                    throw new ApplicationException("Invalid response.");
            }
        }

        // cache the selected item for undo
        _LastSelection = PersonListView.SelectedItem;
    }
于 2009-02-12T18:49:23.430 に答える
2

Selected/Unselected を選択する代わりに、PreviewMouseDown にフックすることをお勧めします。Selected および Unselected イベントを処理する際の問題は、通知を受け取ったときにイベントが既に発生していることです。すでに発生しているため、キャンセルするものは何もありません。

一方、プレビュー イベントはキャンセル可能です。これはあなたが望む正確なイベントではありませんが、ユーザーが別のノードを選択するのを防ぐ機会を与えてくれます。

于 2009-02-12T17:34:04.560 に答える
1

TreeView から派生したカスタム コントロールを作成し、OnSelectedItemChanged メソッドをオーバーライドできます。

ベースを呼び出す前に、まず CancelEventArgs パラメーターを使用してカスタム イベントを発生させることができます。parameter.Cancel が true になった場合は、ベースを呼び出さずに、代わりに古いアイテムを選択します (OnSelectedItemChanged が再度呼び出されることに注意してください)。

最善の解決策ではありませんが、少なくともこれによりロジックがツリー コントロール内に保持され、選択変更イベントが必要以上に発生する可能性がなくなります。また、ユーザーがツリーをクリックしたか、キーボードを使用したか、プログラムによって選択が変更されたかを気にする必要はありません。

于 2011-04-11T10:58:03.063 に答える
1

SelectedItemChangedが変更された後にイベントがトリガーされるため、SelectedItemこの時点でイベントを実際にキャンセルすることはできません。

できることは、マウスクリックをリッスンし、SelectedItem変更される前にキャンセルすることです。

于 2009-02-12T17:33:37.917 に答える
0

一度に1つのツリービューと1つのドキュメントを表示するために、この問題を解決しました。このソリューションは、通常のツリービューにアタッチできるアタッチ可能な動作に基づいています。

<TreeView Grid.Column="0"
       ItemsSource="{Binding TreeViewItems}"
       behav:TreeViewSelectionChangedBehavior.ChangedCommand="{Binding SelectItemChangedCommand}"
       >
     <TreeView.ItemTemplate>
         <HierarchicalDataTemplate ItemsSource="{Binding Children}">
             <StackPanel Orientation="Horizontal">
                 <TextBlock Text="{Binding Name}"
                         ToolTipService.ShowOnDisabled="True"
                         VerticalAlignment="Center" Margin="3" />
             </StackPanel>
         </HierarchicalDataTemplate>
     </TreeView.ItemTemplate>
     <TreeView.ItemContainerStyle>
         <Style TargetType="{x:Type TreeViewItem}">
             <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
             <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
         </Style>
     </TreeView.ItemContainerStyle>
</TreeView>

動作のコードは次のとおりです。

/// <summary>
/// Source:
/// http://stackoverflow.com/questions/1034374/drag-and-drop-in-mvvm-with-scatterview
/// http://social.msdn.microsoft.com/Forums/de-DE/wpf/thread/21bed380-c485-44fb-8741-f9245524d0ae
/// 
/// Attached behaviour to implement the SelectionChanged command/event via delegate command binding or routed commands.
/// </summary>
public static class TreeViewSelectionChangedBehavior
{
#region fields
/// <summary>
/// Field of attached ICommand property
/// </summary>
private static readonly DependencyProperty ChangedCommandProperty = DependencyProperty.RegisterAttached(
  "ChangedCommand",
  typeof(ICommand),
  typeof(TreeViewSelectionChangedBehavior),
  new PropertyMetadata(null, OnSelectionChangedCommandChange));

/// <summary>
/// Implement backing store for UndoSelection dependency proeprty to indicate whether selection should be
/// cancelled via MessageBox query or not.
/// </summary>
public static readonly DependencyProperty UndoSelectionProperty =
  DependencyProperty.RegisterAttached("UndoSelection",
  typeof(bool),
  typeof(TreeViewSelectionChangedBehavior),
  new PropertyMetadata(false, OnUndoSelectionChanged));
#endregion fields

#region methods
#region ICommand changed methods
/// <summary>
/// Setter method of the attached ChangedCommand <seealso cref="ICommand"/> property
/// </summary>
/// <param name="source"></param>
/// <param name="value"></param>
public static void SetChangedCommand(DependencyObject source, ICommand value)
{
  source.SetValue(ChangedCommandProperty, value);
}

/// <summary>
/// Getter method of the attached ChangedCommand <seealso cref="ICommand"/> property
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
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

/// <summary>
/// This method is hooked in the definition of the <seealso cref="ChangedCommandProperty"/>.
/// It is called whenever the attached property changes - in our case the event of binding
/// and unbinding the property to a sink is what we are looking for.
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
private static void OnSelectionChangedCommandChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  TreeView uiElement = d as TreeView;  // Remove the handler if it exist to avoid memory leaks

  if (uiElement != null)
  {
      uiElement.SelectedItemChanged -= Selection_Changed;

      var command = e.NewValue as ICommand;
      if (command != null)
      {
          // the property is attached so we attach the Drop event handler
          uiElement.SelectedItemChanged += Selection_Changed;
      }
  }
}

/// <summary>
/// This method is called when the selection changed event occurs. The sender should be the control
/// on which this behaviour is attached - so we convert the sender into a <seealso cref="UIElement"/>
/// and receive the Command through the <seealso cref="GetChangedCommand"/> getter listed above.
/// 
/// The <paramref name="e"/> parameter contains the standard EventArgs data,
/// which is unpacked and reales upon the bound command.
/// 
/// This implementation supports binding of delegate commands and routed commands.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void Selection_Changed(object sender, RoutedPropertyChangedEventArgs<object> e)
{
  var uiElement = sender as TreeView;

  // Sanity check just in case this was somehow send by something else
  if (uiElement == null)
      return;

  ICommand changedCommand = TreeViewSelectionChangedBehavior.GetChangedCommand(uiElement);

  // There may not be a command bound to this after all
  if (changedCommand == null)
      return;

  // Check whether this attached behaviour is bound to a RoutedCommand
  if (changedCommand is RoutedCommand)
  {
      // Execute the routed command
      (changedCommand as RoutedCommand).Execute(e.NewValue, uiElement);
  }
  else
  {
      // Execute the Command as bound delegate
      changedCommand.Execute(e.NewValue);
  }
}

/// <summary>
/// Executes when the bound boolean property indicates that a user should be asked
/// about changing a treeviewitem selection instead of just performing it.
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
private static void OnUndoSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  TreeView uiElement = d as TreeView;  // Remove the handler if it exist to avoid memory leaks

  if (uiElement != null)
  {
      uiElement.PreviewMouseDown -= uiElement_PreviewMouseDown;

      var command = (bool)e.NewValue;
      if (command == true)
      {
          // the property is attached so we attach the Drop event handler
          uiElement.PreviewMouseDown += uiElement_PreviewMouseDown;
      }
  }
}

/// <summary>
/// Based on the solution proposed here:
/// Source: http://stackoverflow.com/questions/20244916/wpf-treeview-selection-change
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void uiElement_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
  // first did the user click on a tree node?
  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;

  // Cancel the attempt to select an item.
  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)
  {
      // Cancel the attempt to select a differnet item.
      e.Handled = true;
  }
  else
  {
      // Lets disable this for a moment, otherwise, we'll get into an event "recursion"
      treeView.PreviewMouseDown -= uiElement_PreviewMouseDown;

      // Select the new item - make sure a SelectedItemChanged event is fired in any case
      // Even if this means that we have to deselect/select the one and the same item
      if (itemSource.IsSelected == true )
          itemSource.IsSelected = false;

      itemSource.IsSelected = true;

      // Lets enable this to get back to business for next selection
      treeView.PreviewMouseDown += uiElement_PreviewMouseDown;
  }
}
#endregion methods
}

この例では、発生時に PreviewMouseDown イベントをブロックするためにブロック メッセージ ボックスを表示しています。次に、選択が取り消されたことを通知するためにイベントが処理されるか、選択されようとしている項目を選択することによってツリービュー自体がイベントを処理できるようにするために処理されません。

ユーザーがとにかく続行することを決定した場合、ビヘイビアはビューモデルでバインドされたコマンドを呼び出します (PreviewMouseDown イベントは添付されたビヘイビアによって処理されず、バインドされたコマンドが呼び出されます。

メッセージボックスの表示は他の方法でもできると思いますが、イベントが発生したときにイベントをブロックすることが不可欠だと思います。そうしないとキャンセルできないからです(?)。したがって、このコードについて考えられる唯一の改善点は、いくつかの文字列をバインドして、表示されるメッセージを構成可能にすることです。

ダウンロード可能なサンプルを含む記事を書きましたが、それ以外の場合は説明が難しいためです (不足している部分について多くの仮定を立てる必要があり、すべての読者が常に共有しているとは限りません)。

これが私の結果を含む記事です: http://www.codeproject.com/Articles/995629/Cancelable-TreeView-Navigation-for-Documents-in-WP

この解決策についてコメントし、改善の余地があればお知らせください。

于 2015-06-11T00:43:59.393 に答える