2

对于 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,但在互联网上找不到任何有用的东西。

4

3 回答 3

2

我认为更好的解决方案是使用带有 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 --> 允许你将按钮映射到视图模型

你的 xml 看起来像这样

<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 应该保留以供以后使用,我已经将它包含在上面的代码片段中

    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));
            }
        }
    }
于 2012-06-25T14:23:03.443 回答
1

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

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

于 2012-06-25T14:04:52.197 回答
1

我将导航VisualTree以找到 parent 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命令应该指向 中的 a CommandViewModel并且它应该将 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>
于 2012-06-25T14:06:47.610 回答