【问题标题】:How can I access a label from a stackpanel that is a listBoxItem如何从作为 listBoxItem 的堆栈面板访问标签
【发布时间】:2012-06-25 13:23:49
【问题描述】:

对于 WPF 中的列表框,我有这个模板:

<ControlTemplate x:Key="ListBoxItemControlTemplate1" TargetType="{x:Type ListBoxItem}">
    <StackPanel Orientation="Horizontal" Background="Silver">
        <CheckBox Content="CheckBox" VerticalAlignment="Center"/>
        <Label Content="Label" Padding="5,0" Width="260" VerticalAlignment="Center" Background="#F3D6D6D6" Margin="5,0"/>
        <Button Content="Edit" Width="Auto" Padding="1" Margin="2.5,0" HorizontalAlignment="Right" Click="Button_Click"/>
    </StackPanel>
</ControlTemplate>

每当我按下 listBoxItem 的相应按钮时,我都想修改同一个 listBoxItem 的标签,如果可能的话最好不使用名称。
我在想也许有一种方法可以说“使用此按钮父级的标签”,我认为这将是 StackPanel,但在互联网上找不到任何有用的东西。

【问题讨论】:

    标签: c# wpf controls parent


    【解决方案1】:

    我认为更好的解决方案是使用带有 DataTemplate 的视图模型,一旦你设置了代码,你就可以反复使用它,而且出错的可能性很小。

    这是您的视图模型的外观

    public class ViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<ItemViewModel> _items;
    
        public ViewModel()
        {
            _items = new ObservableCollection<ItemViewModel>(new List<ItemViewModel>()
                {
                    new ItemViewModel() { Label = "Item1", IsChecked = false },
                    new ItemViewModel() { Label = "Item2", IsChecked = true },
                    new ItemViewModel() { Label = "Item3", IsChecked = true },
                    new ItemViewModel() { Label = "Item4", IsChecked = false },
                    new ItemViewModel() { Label = "Item5", IsChecked = false },
                });
    
        }
    
        public ObservableCollection<ItemViewModel> Items
        {
            get
            {
    
                return this._items;
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        // Create the OnPropertyChanged method to raise the event
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
    
    public class ItemViewModel  : INotifyPropertyChanged
    {
        private bool _isChecked = false;
        private string _label = "Label";
    
        public ICommand ButtonCommand { get; private set; }
    
        public ItemViewModel()
        {
            this.ButtonCommand = new DelegateCommand(Com_ButtonCommand);
        }
    
        public void Com_ButtonCommand(object parameter)
        {
            this.Label = "New Label text";
        }
    
        public string Label
        {
            get
            {
                return this._label;
            }
            set
            {
                this._label = value;
                this.OnPropertyChanged("Label");
            }
        }
    
        public bool IsChecked
        {
            get
            {
                return this._isChecked;
            }
            set
            {
                this._isChecked = value;
                this.OnPropertyChanged("IsChecked");
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        // Create the OnPropertyChanged method to raise the event
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
    public class DelegateCommand : ICommand
    {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _execute;
    
        public event EventHandler CanExecuteChanged;
    
        public DelegateCommand(Action<object> execute)
            : this(execute, null)
        {
        }
    
        public DelegateCommand(Action<object> execute,
                       Predicate<object> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        public bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }
    
            return _canExecute(parameter);
        }
    
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    
        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
            {
                CanExecuteChanged(this, EventArgs.Empty);
            }
        }
    }
    

    这里有 3 个类,其中 1 个是助手。

    ViewModel --> 你的主 ViewModel, ItemViewModel --> 每个项目的模型, DelegateCommand --> 允许您将按钮映射到视图模型

    您的 xaml 将如下所示

    <ListBox ItemsSource="{Binding Items}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" Background="Silver">
                            <CheckBox IsChecked="{Binding IsChecked}" Content="CheckBox" VerticalAlignment="Center"/>
                            <Label Content="{Binding Label}" Padding="5,0" Width="260" VerticalAlignment="Center" Background="#F3D6D6D6" Margin="5,0"/>
                            <Button Command="{Binding ButtonCommand}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    

    注意“{Binding}”关键字,这会将每个数据模板“绑定”到其自己的视图模型上具有该名称的成员,在本例中为 IsChecked 和 Label。

    要加载您的 ViewModel,请将以下行添加到您的用户控件中的代码隐藏中(使用 MVVM,您几乎不会接触到用户控件的代码隐藏)。

    this.DataContext = new ViewModel();
    

    当您第一次看到视图模型时,它可能看起来需要做很多工作,但它大多是可重复使用的,并且是执行此类操作 (MVVM) 的事实上的标准,我已经包含了所有必要的代码来帮助您入门。

    下面的类以及DelegateCommand应该保留以备后用,我已经包含在上面的sn-p中了

        public class ViewModelBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            // Create the OnPropertyChanged method to raise the event
            protected void OnPropertyChanged(string name)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(name));
                }
            }
        }
    

    【讨论】:

    • 对于像我这样的初学者来说,这是很多代码,但是您在帖子中表现出奉献精神来帮助我,我会尝试它,虽然它接缝令人生畏,请您推荐一些书籍用 WPF 让我站起来?
    • 它看起来确实有很多代码,但重要的是要注意类 ViewModelBase 和 DelegateCommand 永远不会改变,它们可以保留并重新用于其他视图模型,当您使用 ViewModelBase 时,使 View模型更简单。自从我学习所有这些东西以来已经有一段时间了,当时并没有真正的好书涵盖它,我会推荐任何涵盖 WPF 中的 MVVM 的书作为开始,这是一个很好的起点,然后转向更复杂的xaml 指南
    【解决方案2】:

    我建议您在后面使用视图模型。该虚拟机公开了一个绑定到按钮的命令。此外,它还公开了一个包含标签名称的 DependencyProperty。并且标签绑定到该属性。 现在,如果您按下按钮,将执行命令,更改标签文本并通过数据绑定更新标签上的新文本。

    我不推荐的另一个选项是使用 FindName 来查找标签。或者非常糟糕(但有效)是使用 VisualTreeHelper 迭代控件。

    【讨论】:

    • 模型-视图-视图模型对我来说仍然是一个陌生的概念,因为我没有学习任何课程,只是阅读我能找到的书籍并尝试自学。但是非常感谢您的回答,我会花更多时间研究这些元素。
    【解决方案3】:

    我将导航 VisualTree 以找到父 StackPanel,然后搜索 StackPanel 以找到要更新的子 Label

    如果您有兴趣,我在我的博客上发布了一些 VisualTreeHelpers,这将使这变得容易:

    var parent = VisualTreeHelpers.FindAncestor<StackPanel>((Button)sender);
    if (parent == null) return;
    
    var lbl = VisualTreeHelpers.FindChild<Label>(parent);
    if (lbl == null) return;
    
    lbl.Content = "Some Text";
    

    这是假设我没有使用 MVVM 设计模式。如果我使用 MVVM,我会将Label.Content 属性存储在ViewModel 中,Button 命令应该指向ViewModel 中的Command,它应该将DataBound 项作为CommandParameter 所以它知道要更新哪个标签。

    <ControlTemplate x:Key="ListBoxItemControlTemplate1" TargetType="{x:Type ListBoxItem}">
        <StackPanel Orientation="Horizontal" Background="Silver">
            <CheckBox Content="CheckBox" VerticalAlignment="Center"/>
            <Label Content="{Binding SomeText}" ... />
            <Button Content="Edit" 
                    Command="{Binding ElementName=MyListBox, Path=DataContext.EditCommand}"
                    CommandParameter="{Binding }" />
        </StackPanel>
    </ControlTemplate>
    

    【讨论】:

    • 非常感谢,我在互联网上看到人们使用 VisualTree,但我无法使其工作,我将尝试您的 snip 代码,看看是否有什么适合我的方式:P
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-05
    • 1970-01-01
    • 2019-02-03
    • 2011-07-11
    相关资源
    最近更新 更多