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