【问题标题】:WPF TreeView SelectedItemChanged not firingWPF TreeView SelectedItemChanged 未触发
【发布时间】:2011-09-29 00:18:17
【问题描述】:

我正在尝试创建一个允许用户重命名 TreeView 中的节点的 TreeView。该树表示一条 HL7 消息,并按层次结构从段到子组件。

例如:

PID
   PID.1
   PID.2
   etc...

我需要允许用户选择一个节点,按 F2 使该节点进入编辑模式。因为 HL7 允许重复消息结构,所以我还需要 SelectedItem 以便在存在重复名称的情况下知道哪个节点被更改。

目前,每个节点都是一个将 IsReadOnly 设置为 true 的 TextBox,并且被样式化为看起来像一个 TextBlock。当用户按下 F2 时,我对 TextBox 进行样式化,使其看起来像通常用于输入的那样。问题是,TextBox 正在吃掉所有的鼠标事件,阻止 TreeView 设置 SelectedItem 或引发 SelectedItemChanged。

我在 MSDN 上发现了一些讨论,其中有人说在 TextBox 上使用 PreviewMouseLeftButtonDown 事件。我正在使用它,而 TextBox 仍在使用该事件。

有没有人遇到过这个问题或者有什么建议?

【问题讨论】:

  • 找到了解决方案。我很惊讶我还没有在任何地方找到这个。无论如何,如果您使用绑定,请处理 GotFocus 或 PreviewMouseLeftButtonDown 事件,将发送方作为 TextBox 转换为本地对象。从中您可以访问文本框对象的 DataContext 成员。这将表示绑定到 TreeViewItems 的数据对象。

标签: .net wpf c#-4.0


【解决方案1】:

另一种方法是有一个 TextBlock 用于显示和一个隐藏的 TextBox 用于编辑。在将接收键盘事件的 TreeView 上侦听 F2,因为 TextBox 在隐藏时不会获得任何输入焦点。按下 F2 时,隐藏 TextBlock 并显示 TextBox 进行编辑。处理 TextBox 上的 LostFocus 事件,隐藏 TextBox 并再次显示 TextBlock。

这样做的一个好处是您不必伪造 TextBox 使其看起来和表现得像一个TextBlock。 TextBlock 的外观和行为始终与 TextBlock 相似,TextBox 的外观和行为始终与 TextBox 相似,并且它们都可以继承在更高资源级别应用的任何样式。

编辑:添加一些示例代码。

这是 XAML:

<Window.Resources>

    <Style x:Key="TreeViewTextBlockStyle" TargetType="TextBlock">
        <Setter Property="Text" Value="{Binding DisplayText}"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding InEditMode}" Value="true">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>

    <Style x:Key="TreeViewTextBoxStyle" TargetType="TextBox">
        <Setter Property="Text" Value="{Binding DisplayText, Mode=TwoWay}"/>
        <Setter Property="MinWidth" Value="50"/>
        <EventSetter Event="LostFocus" Handler="TreeViewTextBox_LostFocus" />
        <Style.Triggers>
            <DataTrigger Binding="{Binding InEditMode}" Value="false">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding InEditMode}" Value="true">
                <Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Mode=Self}}"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>

    <HierarchicalDataTemplate x:Key="HL7MessageTemplate" ItemsSource="{Binding Segments}">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="[msg]--" FontWeight="Bold"/>
            <TextBlock Style="{StaticResource TreeViewTextBlockStyle}"/>
            <TextBox Style="{StaticResource TreeViewTextBoxStyle}" />
        </StackPanel>
    </HierarchicalDataTemplate>

    <DataTemplate x:Key="HL7SegmentTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="[seg]--" FontWeight="Bold"/>
            <TextBlock Style="{StaticResource TreeViewTextBlockStyle}"/>
            <TextBox Style="{StaticResource TreeViewTextBoxStyle}" />
        </StackPanel>
    </DataTemplate>

    <HierarchicalDataTemplate x:Key="HL7SegmentWithSubcomponentsTemplate" ItemsSource="{Binding Subcomponents}">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="[seg+sub]--" FontWeight="Bold"/>
            <TextBlock Style="{StaticResource TreeViewTextBlockStyle}"/>
            <TextBox Style="{StaticResource TreeViewTextBoxStyle}" />
        </StackPanel>
    </HierarchicalDataTemplate>

    <DataTemplate x:Key="HL7SubcomponentTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="[sub]--" FontWeight="Bold"/>
            <TextBlock Style="{StaticResource TreeViewTextBlockStyle}"/>
            <TextBox Style="{StaticResource TreeViewTextBoxStyle}" />
        </StackPanel>
    </DataTemplate>

    <local:HL7DataTemplateSelector x:Key="HL7DataTemplateSelector"/>

</Window.Resources>    
<Grid>
    <TreeView Name="treeView1" ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource HL7DataTemplateSelector}" KeyUp="treeView1_KeyUp"/>
</Grid>

下面是代码:

private void treeView1_KeyUp(object sender, KeyEventArgs e)
{
    if (e.Key == Key.F2)
    {
        HL7Object selectedHL7Object = treeView1.SelectedItem as HL7Object;
        if (selectedHL7Object != null)
        {
            selectedHL7Object.InEditMode = true;
        }
    }
}

private void TreeViewTextBox_LostFocus(object sender, RoutedEventArgs e)
{
    HL7Object selectedHL7Object = treeView1.SelectedItem as HL7Object;
    if (selectedHL7Object != null)
    {
        selectedHL7Object.InEditMode = false;
    }
}

此代码假定您的 HL7Object 是数据对象的基类,如下所示:

public class HL7Object : INotifyPropertyChanged
{
    private string DisplayTextField;
    public string DisplayText
    {
        get { return this.DisplayTextField; }
        set
        {
            if (this.DisplayTextField != value)
            {
                this.DisplayTextField = value;
                this.OnPropertyChanged("DisplayText");
            }
        }
    }

    private bool InEditModeField = false;
    public bool InEditMode
    {
        get { return this.InEditModeField; }
        set
        {
            if (this.InEditModeField != value)
            {
                this.InEditModeField = value;
                this.OnPropertyChanged("InEditMode");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

而且您还实现了一个 DataTemplateSelector,由于您的复杂要求,我假设您已经实现了它。如果没有,这里是一个例子:

public class HL7DataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        FrameworkElement element = container as FrameworkElement;

        if (element != null && item != null &&
            (item is HL7Message || item is HL7Segment || item is HL7Subcomponent)
            )
        {
            HL7Message message = item as HL7Message;
            if (message != null)
            {
                return element.FindResource("HL7MessageTemplate") as DataTemplate;
            }

            HL7Segment segment = item as HL7Segment;
            if (segment != null)
            {
                if (segment.Subcomponents != null && segment.Subcomponents.Count > 0)
                {
                    return element.FindResource("HL7SegmentWithSubcomponentsTemplate") as DataTemplate;
                }
                else
                {
                    return element.FindResource("HL7SegmentTemplate") as DataTemplate;
                }
            }

            HL7Subcomponent subcomponent = item as HL7Subcomponent;
            if (subcomponent != null)
            {
                return element.FindResource("HL7SubcomponentTemplate") as DataTemplate;
            }
        }

        return null;
    }
}

【讨论】:

  • 不幸的是,这个解决方案不适用于我的情况。由于树中嵌套的复杂性,我们不得不将多个分层数据模板放入树视图资源中。我无法找到文本块或文本框,因为它们不在树视图项目模板中。即使我们在树视图项模板中使用一个分层数据模板,我更喜欢我找到的解决方案,因为有一个为 IsReadOnly 设置样式的文本框看起来像一个 TextBlock,我没有维护第二个字段的开销。我会接受它作为答案,因为它对其他人有用。
  • 虽然实际上可以使用 treeView.ItemContainerGenerator.ContainerFromItem(treeView.SelectedItem) 找到文本块/文本框并使用 VisualTreeHelper 遍历可视化树,同时有效地使用 WPF 样式和模板不需要找到它们(我将在上面的答案中添加一些示例代码)。此外,您不需要维护第二个字段,只需将两个控件数据绑定到同一个属性即可。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-05
  • 1970-01-01
  • 1970-01-01
  • 2020-10-08
  • 2017-07-01
  • 1970-01-01
相关资源
最近更新 更多