WPF TreeView 鼠标按下
我在 TreeView 中有类似的东西:
<DataTemplate x:Key="myTemplate">
<StackPanel MouseDown="OnItemMouseDown">
...
</StackPanel>
</DataTemplate>
使用它,如果我单击堆栈面板中的项目,我会收到鼠标按下事件。然而......堆栈面板后面似乎还有另一个项目,即 TreeViewItem - 它很难击中,但并非不可能,这就是问题开始发生的时候。
我尝试过处理 TreeViewItem 上的 PreviewMouseDown,但这似乎需要
e.Handled = false
其他标准树视图行为停止工作。
好的,这是源代码...
MainWindow.xaml
<Window x:Class="WPFMultiSelectTree.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFMultiSelectTree"
Title="Multiple Selection Tree" Height="300" Width="300">
<Window.Resources>
<!-- Declare the classes that convert bool to Visibility -->
<local:VisibilityConverter x:Key="visibilityConverter"/>
<local:VisibilityInverter x:Key="visibilityInverter"/>
<!-- Set the style for any tree view item -->
<Style TargetType="TreeViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Selected}" Value="True">
<Setter Property="Background" Value="DarkBlue"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
<EventSetter Event="PreviewMouseDown" Handler="OnTreePreviewMouseDown"/>
</Style>
<!-- Declare a hierarchical data template for the tree view items -->
<HierarchicalDataTemplate
x:Key="RecursiveTemplate"
ItemsSource="{Binding Children}">
<StackPanel Margin="2" Orientation="Horizontal" MouseDown="OnTreeMouseDown">
<Ellipse Width="12" Height="12" Fill="Green"/>
<TextBlock
Margin="2"
Text="{Binding Name}"
Visibility="{Binding Editing, Converter={StaticResource visibilityInverter}}"/>
<TextBox
Margin="2"
Text="{Binding Name}"
KeyDown="OnTextBoxKeyDown"
IsVisibleChanged="OnTextBoxIsVisibleChanged"
Visibility="{Binding Editing, Converter={StaticResource visibilityConverter}}"/>
<TextBlock
Margin="2"
Text="{Binding Index, StringFormat=({0})}"/>
</StackPanel>
</HierarchicalDataTemplate>
<!-- Declare a simple template for a list box -->
<DataTemplate x:Key="ListTemplate">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<!-- Declare the rows in this grid -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- The first header -->
<TextBlock Grid.Row="0" Margin="5" Background="PowderBlue">Multiple selection tree view</TextBlock>
<!-- The tree view -->
<TreeView
Name="m_tree"
Margin="2"
Grid.Row="1"
ItemsSource="{Binding Children}"
ItemTemplate="{StaticResource RecursiveTemplate}"/>
<!-- The second header -->
<TextBlock Grid.Row="2" Margin="5" Background="PowderBlue">The currently selected items in the tree</TextBlock>
<!-- The list box -->
<ListBox
Name="m_list"
Margin="2"
Grid.Row="3"
ItemsSource="{Binding .}"
ItemTemplate="{StaticResource ListTemplate}"/>
</Grid>
</Window>
MainWindow.xaml.cs
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Container m_root;
private Container m_first;
private ObservableCollection<Container> m_selection;
private string m_current;
/// <summary>
/// Constructor
/// </summary>
public MainWindow()
{
InitializeComponent();
m_selection = new ObservableCollection<Container>();
m_root = new Container("root");
for (int parents = 0; parents < 50; parents++)
{
Container parent = new Container(String.Format("parent{0}", parents + 1));
for (int children = 0; children < 1000; children++)
{
parent.Add(new Container(String.Format("child{0}", children + 1)));
}
m_root.Add(parent);
}
m_tree.DataContext = m_root;
m_list.DataContext = m_selection;
m_first = null;
}
/// <summary>
/// Has the shift key been pressed?
/// </summary>
private bool ShiftPressed
{
get
{
return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
}
}
/// <summary>
/// Has the control key been pressed?
/// </summary>
private bool CtrlPressed
{
get
{
return Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
}
}
/// <summary>
/// Clear down the selection list
/// </summary>
private void DeselectAndClear()
{
foreach(Container container in m_selection)
{
container.Selected = false;
}
m_selection.Clear();
}
/// <summary>
/// Add the container to the list (if not already present),
/// mark as selected
/// </summary>
/// <param name="container"></param>
private void AddToSelection(Container container)
{
if (container == null)
{
return;
}
foreach (Container child in m_selection)
{
if (child == container)
{
return;
}
}
container.Selected = true;
m_selection.Add(container);
}
/// <summary>
/// Remove container from list, mark as not selected
/// </summary>
/// <param name="container"></param>
private void RemoveFromSelection(Container container)
{
m_selection.Remove(container);
container.Selected = false;
}
/// <summary>
/// Process single click on a tree item
///
/// Normally just select an item
///
/// SHIFT-Click extends selection
/// CTRL-Click toggles a selection
/// </summary>
/// <param name="sender"></param>
private void OnTreeSingleClick(object sender)
{
FrameworkElement element = sender as FrameworkElement;
if (element != null)
{
Container container = element.DataContext as Container;
if (container != null)
{
if (CtrlPressed)
{
if (container.Selected)
{
RemoveFromSelection(container);
}
else
{
AddToSelection(container);
}
}
else if (ShiftPressed)
{
if (container.Parent == m_first.Parent)
{
if (container.Index < m_first.Index)
{
Container item = container;
for (int i = container.Index; i < m_first.Index; i++)
{
AddToSelection(item);
item = item.Next;
if (item == null)
{
break;
}
}
}
else if (container.Index > m_first.Index)
{
Container item = m_first;
for (int i = m_first.Index; i <= container.Index; i++)
{
AddToSelection(item);
item = item.Next;
if (item == null)
{
break;
}
}
}
}
}
else
{
DeselectAndClear();
m_first = container;
AddToSelection(container);
}
}
}
}
/// <summary>
/// Process double click on tree item
/// </summary>
/// <param name="sender"></param>
private void OnTreeDoubleClick(object sender)
{
FrameworkElement element = sender as FrameworkElement;
if (element != null)
{
Container container = element.DataContext as Container;
if (container != null)
{
container.Editing = true;
m_current = container.Name;
}
}
}
/// <summary>
/// Clicked on the stack panel in the tree view
///
/// Double left click:
///
/// Switch to editing mode (flips visibility of textblock and textbox)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTreeMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("StackPanel mouse down");
switch(e.ChangedButton)
{
case MouseButton.Left:
switch (e.ClickCount)
{
case 2:
OnTreeDoubleClick(sender);
e.Handled = true;
break;
}
break;
}
}
/// <summary>
/// Clicked on tree view item in tree
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTreePreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("TreeViewItem preview mouse down");
switch (e.ChangedButton)
{
case MouseButton.Left:
switch (e.ClickCount)
{
case 1:
{
// We've had a single click on a tree view item
// Unfortunately this is the WHOLE tree item, including the +/-
// symbol to the left. The tree doesn't do a selection, so we
// have to filter this out...
MouseDevice device = e.Device as MouseDevice;
Debug.WriteLine(String.Format("Tree item clicked on: {0}", device.DirectlyOver.GetType().ToString()));
// This is bad. The whole point of WPF is for the code
// not to know what the UI has - yet here we are testing for
// it as a workaround. Sigh...
if (device.DirectlyOver.GetType() != typeof(Path))
{
OnTreeSingleClick(sender);
}
// Cannot say handled - if we do it stops the tree working!
//e.Handled = true;
}
break;
}
break;
}
}
/// <summary>
/// Key press in text box
///
/// Return key finishes editing
/// Escape key finishes editing, restores original value (this doesn't work!)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
{
switch(e.Key)
{
case Key.Return:
{
TextBox box = sender as TextBox;
if (box != null)
{
Container container = box.DataContext as Container;
if (container != null)
{
container.Editing = false;
e.Handled = true;
}
}
}
break;
case Key.Escape:
{
TextBox box = sender as TextBox;
if (box != null)
{
Container container = box.DataContext as Container;
if (container != null)
{
container.Editing = false;
container.Name = m_current;
e.Handled = true;
}
}
}
break;
}
}
/// <summary>
/// When text box becomes visible, grab focus and select all text in it.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTextBoxIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
bool visible = (bool)e.NewValue;
if (visible)
{
TextBox box = sender as TextBox;
if (box != null)
{
box.Focus();
box.SelectAll();
}
}
}
}
这是 Container 类
public class Container : INotifyPropertyChanged
{
private string m_name;
private ObservableCollection<Container> m_children;
private Container m_parent;
private bool m_selected;
private bool m_editing;
/// <summary>
/// Constructor
/// </summary>
/// <param name="name">name of object</param>
public Container(string name)
{
m_name = name;
m_children = new ObservableCollection<Container>();
m_parent = null;
m_selected = false;
m_editing = false;
}
/// <summary>
/// Name of object
/// </summary>
public string Name
{
get
{
return m_name;
}
set
{
if (m_name != value)
{
m_name = value;
OnPropertyChanged("Name");
}
}
}
/// <summary>
/// Index of object in parent's children
///
/// If there's no parent, the index is -1
/// </summary>
public int Index
{
get
{
if (m_parent != null)
{
return m_parent.Children.IndexOf(this);
}
return -1;
}
}
/// <summary>
/// Get the next item, assuming this is parented
///
/// Returns null if end of list reached, or no parent
/// </summary>
public Container Next
{
get
{
if (m_parent != null)
{
int index = Index + 1;
if (index < m_parent.Children.Count)
{
return m_parent.Children[index];
}
}
return null;
}
}
/// <summary>
/// List of children
/// </summary>
public ObservableCollection<Container> Children
{
get
{
return m_children;
}
}
/// <summary>
/// Selected status
/// </summary>
public bool Selected
{
get
{
return m_selected;
}
set
{
if (m_selected != value)
{
m_selected = value;
OnPropertyChanged("Selected");
}
}
}
/// <summary>
/// Editing status
/// </summary>
public bool Editing
{
get
{
return m_editing;
}
set
{
if (m_editing != value)
{
m_editing = value;
OnPropertyChanged("Editing");
}
}
}
/// <summary>
/// Parent of this object
/// </summary>
public Container Parent
{
get
{
return m_parent;
}
set
{
m_parent = value;
}
}
/// <summary>
/// WPF Property Changed event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Handler to inform WPF that a property has changed
/// </summary>
/// <param name="name"></param>
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// Add a child to this container
/// </summary>
/// <param name="child"></param>
public void Add(Container child)
{
m_children.Add(child);
child.m_parent = this;
}
/// <summary>
/// Remove a child from this container
/// </summary>
/// <param name="child"></param>
public void Remove(Container child)
{
m_children.Remove(child);
child.m_parent = null;
}
}
VisibilityConverter 和 VisibilityInverter 两个类是实现IValueConverter 将 bool 转换为 Visibility。它们确保在不编辑时显示 TextBlock,并在编辑时显示 TextBox。
I've got something like this in a TreeView:
<DataTemplate x:Key="myTemplate">
<StackPanel MouseDown="OnItemMouseDown">
...
</StackPanel>
</DataTemplate>
Using this I get the mouse down events if I click on items in the stack panel. However... there seems to be another item behind the stack panel that is the TreeViewItem - it's very hard to hit, but not impossible, and that's when the problems start to occur.
I had a go at handling PreviewMouseDown on TreeViewItem, however that seems to require
e.Handled = false
otherwise standard tree view behaviour stops working.
Ok, Here's the source code...
MainWindow.xaml
<Window x:Class="WPFMultiSelectTree.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFMultiSelectTree"
Title="Multiple Selection Tree" Height="300" Width="300">
<Window.Resources>
<!-- Declare the classes that convert bool to Visibility -->
<local:VisibilityConverter x:Key="visibilityConverter"/>
<local:VisibilityInverter x:Key="visibilityInverter"/>
<!-- Set the style for any tree view item -->
<Style TargetType="TreeViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Selected}" Value="True">
<Setter Property="Background" Value="DarkBlue"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
<EventSetter Event="PreviewMouseDown" Handler="OnTreePreviewMouseDown"/>
</Style>
<!-- Declare a hierarchical data template for the tree view items -->
<HierarchicalDataTemplate
x:Key="RecursiveTemplate"
ItemsSource="{Binding Children}">
<StackPanel Margin="2" Orientation="Horizontal" MouseDown="OnTreeMouseDown">
<Ellipse Width="12" Height="12" Fill="Green"/>
<TextBlock
Margin="2"
Text="{Binding Name}"
Visibility="{Binding Editing, Converter={StaticResource visibilityInverter}}"/>
<TextBox
Margin="2"
Text="{Binding Name}"
KeyDown="OnTextBoxKeyDown"
IsVisibleChanged="OnTextBoxIsVisibleChanged"
Visibility="{Binding Editing, Converter={StaticResource visibilityConverter}}"/>
<TextBlock
Margin="2"
Text="{Binding Index, StringFormat=({0})}"/>
</StackPanel>
</HierarchicalDataTemplate>
<!-- Declare a simple template for a list box -->
<DataTemplate x:Key="ListTemplate">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<!-- Declare the rows in this grid -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- The first header -->
<TextBlock Grid.Row="0" Margin="5" Background="PowderBlue">Multiple selection tree view</TextBlock>
<!-- The tree view -->
<TreeView
Name="m_tree"
Margin="2"
Grid.Row="1"
ItemsSource="{Binding Children}"
ItemTemplate="{StaticResource RecursiveTemplate}"/>
<!-- The second header -->
<TextBlock Grid.Row="2" Margin="5" Background="PowderBlue">The currently selected items in the tree</TextBlock>
<!-- The list box -->
<ListBox
Name="m_list"
Margin="2"
Grid.Row="3"
ItemsSource="{Binding .}"
ItemTemplate="{StaticResource ListTemplate}"/>
</Grid>
</Window>
MainWindow.xaml.cs
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Container m_root;
private Container m_first;
private ObservableCollection<Container> m_selection;
private string m_current;
/// <summary>
/// Constructor
/// </summary>
public MainWindow()
{
InitializeComponent();
m_selection = new ObservableCollection<Container>();
m_root = new Container("root");
for (int parents = 0; parents < 50; parents++)
{
Container parent = new Container(String.Format("parent{0}", parents + 1));
for (int children = 0; children < 1000; children++)
{
parent.Add(new Container(String.Format("child{0}", children + 1)));
}
m_root.Add(parent);
}
m_tree.DataContext = m_root;
m_list.DataContext = m_selection;
m_first = null;
}
/// <summary>
/// Has the shift key been pressed?
/// </summary>
private bool ShiftPressed
{
get
{
return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
}
}
/// <summary>
/// Has the control key been pressed?
/// </summary>
private bool CtrlPressed
{
get
{
return Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
}
}
/// <summary>
/// Clear down the selection list
/// </summary>
private void DeselectAndClear()
{
foreach(Container container in m_selection)
{
container.Selected = false;
}
m_selection.Clear();
}
/// <summary>
/// Add the container to the list (if not already present),
/// mark as selected
/// </summary>
/// <param name="container"></param>
private void AddToSelection(Container container)
{
if (container == null)
{
return;
}
foreach (Container child in m_selection)
{
if (child == container)
{
return;
}
}
container.Selected = true;
m_selection.Add(container);
}
/// <summary>
/// Remove container from list, mark as not selected
/// </summary>
/// <param name="container"></param>
private void RemoveFromSelection(Container container)
{
m_selection.Remove(container);
container.Selected = false;
}
/// <summary>
/// Process single click on a tree item
///
/// Normally just select an item
///
/// SHIFT-Click extends selection
/// CTRL-Click toggles a selection
/// </summary>
/// <param name="sender"></param>
private void OnTreeSingleClick(object sender)
{
FrameworkElement element = sender as FrameworkElement;
if (element != null)
{
Container container = element.DataContext as Container;
if (container != null)
{
if (CtrlPressed)
{
if (container.Selected)
{
RemoveFromSelection(container);
}
else
{
AddToSelection(container);
}
}
else if (ShiftPressed)
{
if (container.Parent == m_first.Parent)
{
if (container.Index < m_first.Index)
{
Container item = container;
for (int i = container.Index; i < m_first.Index; i++)
{
AddToSelection(item);
item = item.Next;
if (item == null)
{
break;
}
}
}
else if (container.Index > m_first.Index)
{
Container item = m_first;
for (int i = m_first.Index; i <= container.Index; i++)
{
AddToSelection(item);
item = item.Next;
if (item == null)
{
break;
}
}
}
}
}
else
{
DeselectAndClear();
m_first = container;
AddToSelection(container);
}
}
}
}
/// <summary>
/// Process double click on tree item
/// </summary>
/// <param name="sender"></param>
private void OnTreeDoubleClick(object sender)
{
FrameworkElement element = sender as FrameworkElement;
if (element != null)
{
Container container = element.DataContext as Container;
if (container != null)
{
container.Editing = true;
m_current = container.Name;
}
}
}
/// <summary>
/// Clicked on the stack panel in the tree view
///
/// Double left click:
///
/// Switch to editing mode (flips visibility of textblock and textbox)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTreeMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("StackPanel mouse down");
switch(e.ChangedButton)
{
case MouseButton.Left:
switch (e.ClickCount)
{
case 2:
OnTreeDoubleClick(sender);
e.Handled = true;
break;
}
break;
}
}
/// <summary>
/// Clicked on tree view item in tree
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTreePreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("TreeViewItem preview mouse down");
switch (e.ChangedButton)
{
case MouseButton.Left:
switch (e.ClickCount)
{
case 1:
{
// We've had a single click on a tree view item
// Unfortunately this is the WHOLE tree item, including the +/-
// symbol to the left. The tree doesn't do a selection, so we
// have to filter this out...
MouseDevice device = e.Device as MouseDevice;
Debug.WriteLine(String.Format("Tree item clicked on: {0}", device.DirectlyOver.GetType().ToString()));
// This is bad. The whole point of WPF is for the code
// not to know what the UI has - yet here we are testing for
// it as a workaround. Sigh...
if (device.DirectlyOver.GetType() != typeof(Path))
{
OnTreeSingleClick(sender);
}
// Cannot say handled - if we do it stops the tree working!
//e.Handled = true;
}
break;
}
break;
}
}
/// <summary>
/// Key press in text box
///
/// Return key finishes editing
/// Escape key finishes editing, restores original value (this doesn't work!)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
{
switch(e.Key)
{
case Key.Return:
{
TextBox box = sender as TextBox;
if (box != null)
{
Container container = box.DataContext as Container;
if (container != null)
{
container.Editing = false;
e.Handled = true;
}
}
}
break;
case Key.Escape:
{
TextBox box = sender as TextBox;
if (box != null)
{
Container container = box.DataContext as Container;
if (container != null)
{
container.Editing = false;
container.Name = m_current;
e.Handled = true;
}
}
}
break;
}
}
/// <summary>
/// When text box becomes visible, grab focus and select all text in it.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTextBoxIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
bool visible = (bool)e.NewValue;
if (visible)
{
TextBox box = sender as TextBox;
if (box != null)
{
box.Focus();
box.SelectAll();
}
}
}
}
Here's the Container class
public class Container : INotifyPropertyChanged
{
private string m_name;
private ObservableCollection<Container> m_children;
private Container m_parent;
private bool m_selected;
private bool m_editing;
/// <summary>
/// Constructor
/// </summary>
/// <param name="name">name of object</param>
public Container(string name)
{
m_name = name;
m_children = new ObservableCollection<Container>();
m_parent = null;
m_selected = false;
m_editing = false;
}
/// <summary>
/// Name of object
/// </summary>
public string Name
{
get
{
return m_name;
}
set
{
if (m_name != value)
{
m_name = value;
OnPropertyChanged("Name");
}
}
}
/// <summary>
/// Index of object in parent's children
///
/// If there's no parent, the index is -1
/// </summary>
public int Index
{
get
{
if (m_parent != null)
{
return m_parent.Children.IndexOf(this);
}
return -1;
}
}
/// <summary>
/// Get the next item, assuming this is parented
///
/// Returns null if end of list reached, or no parent
/// </summary>
public Container Next
{
get
{
if (m_parent != null)
{
int index = Index + 1;
if (index < m_parent.Children.Count)
{
return m_parent.Children[index];
}
}
return null;
}
}
/// <summary>
/// List of children
/// </summary>
public ObservableCollection<Container> Children
{
get
{
return m_children;
}
}
/// <summary>
/// Selected status
/// </summary>
public bool Selected
{
get
{
return m_selected;
}
set
{
if (m_selected != value)
{
m_selected = value;
OnPropertyChanged("Selected");
}
}
}
/// <summary>
/// Editing status
/// </summary>
public bool Editing
{
get
{
return m_editing;
}
set
{
if (m_editing != value)
{
m_editing = value;
OnPropertyChanged("Editing");
}
}
}
/// <summary>
/// Parent of this object
/// </summary>
public Container Parent
{
get
{
return m_parent;
}
set
{
m_parent = value;
}
}
/// <summary>
/// WPF Property Changed event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Handler to inform WPF that a property has changed
/// </summary>
/// <param name="name"></param>
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// Add a child to this container
/// </summary>
/// <param name="child"></param>
public void Add(Container child)
{
m_children.Add(child);
child.m_parent = this;
}
/// <summary>
/// Remove a child from this container
/// </summary>
/// <param name="child"></param>
public void Remove(Container child)
{
m_children.Remove(child);
child.m_parent = null;
}
}
The two classes VisibilityConverter and VisibilityInverter are implementations of IValueConverter that translates bool to Visibility. They make sure the TextBlock is displayed when not editing, and the TextBox is displayed when editing.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论