如何从作为listBoxItem的stackpanel访问标签

时间:2012-06-25 13:23:49

标签: c# wpf controls parent

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

3 个答案:

答案 0 :(得分: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 - &gt;你的主要ViewModel, ItemViewModel - &gt;每个项目的型号, DelegateCommand - &gt;允许您将按钮映射到视图模型

你的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,请将以下行添加到usercontrol中的代码隐藏中(使用MVVM,您很少会触及用户控件的代码隐藏)。

this.DataContext = new ViewModel();

当你第一次看到一个视图模型时,看起来似乎有很多工作,但它主要是可重用的,并且是做这样事情的事实标准(MVVM),我已经包含了所有必要的代码来帮助你入门。 / p>

以下类和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));
            }
        }
    }

答案 1 :(得分:1)

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

我不建议使用另一个选项,即使用FindName查找Label。或者非常糟糕(但有效)是使用VisualTreeHelper迭代控件。

答案 2 :(得分:1)

我会浏览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命令应该指向Command中的ViewModel,它应该将DataBound项作为CommandParameter传递给它,以便它知道要更新哪个Label。

<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>