WPF切换数据模板中ui元素的可见性

本文关键字:ui 元素 可见性 数据 WPF | 更新日期: 2023-09-27 18:06:54

我有一个DataTemplate定义如下

<DataTemplate x:Key="PasswordViewerTemplate">
  <StackPanel>
    <TextBlock Text="{Binding PasswordChar, ElementName=this}"
               Visibility="Visible" />
    <TextBox Text="{Binding PasswordText}"
             Visibility="Collapsed" />
  </StackPanel>
</DataTemplate>

我希望能够切换TextBlockTextBox的可见性每次用户点击StackPanel。我尝试在StackPanel上设置MouseLeftButtonUp事件处理程序,但这会抛出异常

Object reference not set to an instance of an object

是否有其他方法来实现这一点?也许在XAML本身使用触发器?

同样,这可能是相关的。上面的模板是模板选择器应用于ListBox的两个模板之一。ListBox本身在Grid中,两个模板都在Grid.Resources部分中定义。

编辑1
我尝试按如下方式设置事件

<StackPanel MouseLeftButtonUp="OnPasswordViewerMouseLeftButtonUp">
...
</StackPanel>
private void OnPasswordViewerMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
  var sp = sender as StackPanel;
  if( ( sp == null ) || ( sp.Children.Count != 2 ) ) {
    return;
  }
  var passwordText = sp.Children[0] as TextBlock;
  var plainText = sp.Children[1] as TextBox;
  if( ( passwordText == null ) || ( plainText == null ) ) {
    return;
  }
  passwordText.Visibility = ( passwordText.Visibility == Visibility.Visible ) ? 
    Visibility.Collapsed : Visibility.Visible;
  plainText.Visibility = ( plainText.Visibility == Visibility.Visible ) ?
    Visibility.Collapsed : Visibility.Visible;
}

WPF切换数据模板中ui元素的可见性

解决方案之一是将TextBoxTextBlock的可见性绑定到类的属性,该类用于StackPanelDataContext。下面是一个示例实现:

Xaml代码:

<Grid>
    <Grid.Resources>
        <DataTemplate x:Key="PasswordViewerTemplate">
            <StackPanel PreviewMouseUp="StackPanel_PreviewMouseUp">
                <TextBlock Text="{Binding Path=PasswordChar}"
           Visibility="{Binding Path=TextBlockVisibility}" />
                <TextBox Text="{Binding Path=PasswordText}"
         Visibility="{Binding Path=TextBoxVisibility}" />
            </StackPanel>
        </DataTemplate>
    </Grid.Resources>
    <ListBox x:Name="lbox" ItemTemplate="{StaticResource ResourceKey=PasswordViewerTemplate}" ItemsSource="{Binding}"/>
</Grid>

c#代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        ObservableCollection<Some> items = new ObservableCollection<Some>();
        for (int i = 0; i < 10; i++)
        {
            items.Add(new Some(string.Format("passwordChar {0}", i + 1), string.Format("passwordText {0}", i + 1), Visibility.Visible, Visibility.Collapsed));
        }
        this.lbox.ItemsSource = items;
    }
    private void StackPanel_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        Some some = (sender as StackPanel).DataContext as Some;
        some.TextBlockVisibility = ToggleVisibility(some.TextBlockVisibility);
        some.TextBoxVisibility = ToggleVisibility(some.TextBoxVisibility);
    }
    private Visibility ToggleVisibility(Visibility visibility)
    {
        return visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
    }
}
public class Some:INotifyPropertyChanged
{
    private string _passwordChar;
    private string _passwordText;
    private Visibility _textBlockVisibility, _textBoxVisibility;
    public string PasswordChar { get { return this._passwordChar; } set { this._passwordChar = value; } }
    public string PasswordText { get { return this._passwordText; } set { this._passwordText = value; } }
    public Visibility TextBlockVisibility 
    { 
        get { return this._textBlockVisibility; } 
        set 
        { 
            this._textBlockVisibility = value;
            RaisePropertyChanged("TextBlockVisibility");
        }
    }
    public Visibility TextBoxVisibility 
    { 
        get { return this._textBoxVisibility; }
        set 
        { 
            this._textBoxVisibility = value;
            RaisePropertyChanged("TextBoxVisibility");
        }
    }
    public Some(string passwordChar, string passwordText, Visibility textBlockVisibility, Visibility textBoxVisibility)
    {
        this._passwordChar = passwordChar;
        this._passwordText = passwordText;
        this._textBlockVisibility = textBlockVisibility;
        this._textBoxVisibility = textBoxVisibility;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

为什么不在视图模型中绑定项的可见性呢?

一个例子。

<Textblock Test="{Binding passwordText,ElementName=This}" Visibility="{Binding passwordTextVisibility}"/>

在ViewModel中写入

public Visibility passwordTextVisibility
{
 getters and setters here
}

和鼠标事件,在堆栈面板中需要某种路由事件。一个例子:

在堆栈面板内,您将需要鼠标。你需要什么都行。阅读一些关于路由事件的知识

的例子。如果PreviewMouseLeftButtonUp不工作

<StackPanel Mouse.MouseUp="MouseButtonUpEventHandler"/>

public void MouseButtonUpEventHandler (RoutedEvent e)
{
//logic here to check if it's left mouse if it is then set visibility
}

}