好吧,这绝对是一个新手问题,不幸的是无法找出/找到答案。
本质上是将对象列表绑定到组合框,当对象上的
Disabled
属性设置为 true 时,我希望组合框项目的文本颜色设置为灰色。
这是我到目前为止所拥有的:
组合框项目数据类型
public class ListItem
{
public ListItem(string text)
{
Text = text;
}
public string Text { get; set; }
public bool Disabled { get; set; }
}
查看模型设置
public class MainPageViewModel : ReactiveObject
{
// In ReactiveUI, this is the syntax to declare a read-write property
// that will notify Observers, as well as WPF, that a property has
// changed. If we declared this as a normal property, we couldn't tell
// when it has changed!
private ListItem _selectedItem;
public ListItem SelectedItem
{
get => _selectedItem;
set => this.RaiseAndSetIfChanged(ref _selectedItem, value);
}
public List<ListItem> Items { get; set; }
public MainPageViewModel()
{
Items = new List<ListItem>
{
new ListItem ("A Cat"),
new ListItem ("A Dog"),
new ListItem ("A Mouse"),
new ListItem ("A Frog") { Disabled = true }
};
}
}
ReactiveUI 绑定
public MainPage()
{
InitializeComponent();
ViewModel = new MainPageViewModel();
this.WhenActivated(d =>
{
this.OneWayBind(ViewModel, vm => vm.Items, v => v.MyComboBox.ItemsSource)
.DisposeWith(d);
this.Bind(ViewModel, vm => vm.SelectedItem, v => v.MyComboBox.SelectedItem)
.DisposeWith(d);
});
}
Xaml 标记
<ComboBox
Name="MyComboBox"
Margin="0,0,0,20"
Foreground="black">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Disabled}" Value="True">
<Setter Property="Foreground" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
感谢任何帮助,如果您需要更多信息,请告诉我。
解决方案:看起来将来我需要在放置之前测试示例代码 - 我们的实际代码将
Disabled
属性设置为 readonly
,这肯定会与 WPF 绑定混淆。将其更改为公共设置并解决了看不到它变灰的第一个问题!盯着一个问题这么久似乎会让你盲目,而事实就是这么简单。
至于将所选项目变灰,我会尝试一下。
下拉列表中的最后一项的文本已经变灰,所以我假设您正在询问所选项目。
ComboBox
对所选项目和下拉列表中的项目使用单独的数据模板。您可以使用 DataTemplateSelector
来设置两者。
public class ComboBoxTemplateSelector : DataTemplateSelector
{
public DataTemplate SelectedItemTemplate { get; set; }
public DataTemplate DropdownItemsTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var itemToCheck = container;
// Search up the visual tree, stopping at either a ComboBox or a ComboBoxItem (or null).
// This will determine which template to use.
while (itemToCheck is not null and not ComboBox and not ComboBoxItem)
itemToCheck = VisualTreeHelper.GetParent(itemToCheck);
// If you stopped at a ComboBoxItem, you're in the dropdown.
return itemToCheck is ComboBoxItem ? DropdownItemsTemplate : SelectedItemTemplate;
}
}
<StackPanel>
<StackPanel.Resources>
<Style x:Key="GrayedOutText" TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Disabled}" Value="True">
<Setter Property="Foreground" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
<local:ComboBoxTemplateSelector x:Key="ComboBoxTemplateSelector">
<local:ComboBoxTemplateSelector.SelectedItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Style="{StaticResource GrayedOutText}" />
</DataTemplate>
</local:ComboBoxTemplateSelector.SelectedItemTemplate>
<local:ComboBoxTemplateSelector.DropdownItemsTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Style="{StaticResource GrayedOutText}" />
</DataTemplate>
</local:ComboBoxTemplateSelector.DropdownItemsTemplate>
</local:ComboBoxTemplateSelector>
</StackPanel.Resources>
<ComboBox
Name="MyComboBox"
Margin="0,0,0,20"
ItemTemplateSelector="{StaticResource ComboBoxTemplateSelector}">
</ComboBox>
</StackPanel>
我们在
DataTemplate
定义中有一些重复,但这些在生产代码中往往会分开。
我假设您的问题是应用程序运行后
ComboBoxItem
不会变灰。
我不熟悉ReactiveUI,但由于我在您的代码中发现了问题,所以我在您的代码的CommunityToolkit.Mvvm版本中进行了尝试并验证了我的理论。
最下面,您需要将 INotifyPropertyChanged
的
ReactiveUI版本实现到
Disabled
属性。
如果您有兴趣,我可以发布此代码的 CommunityToolkit.Mvvm 版本。
这是在我的测试中有效的方法:
组合框项目数据类型:
//-- Unchanged
public class ListItem
{
public ListItem( string text )
{
Text = text;
}
public string Text { get; set; }
public bool Disabled { get; set; }
}
视图模型设置:
public class MainPageViewModel : INotifyPropertyChanged
{
private ListItem? _selectedItem;
public event PropertyChangedEventHandler? PropertyChanged;
public ListItem? SelectedItem
{
get => _selectedItem;
set
{
//-- I didn't had the "RaiseAndSetIfChanged" method, so I just implemented the functionality manually
if( value != _selectedItem )
{
//-- Update the value ...
_selectedItem = value;
//-- ... AND inform everyone (who is interested) about the change
this.PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( nameof( this.SelectedItem ) ) );
}
}
}
//-- Use always an ObservableCollection when you want to achieve reactivity
public ObservableCollection<ListItem> Items
{ get; } = new ObservableCollection<ListItem>();
public MainPageViewModel()
{
//-- Add some test data
this.Items.Add( new ListItem( "A Cat" ) );
this.Items.Add( new ListItem( "A Dog" ) );
this.Items.Add( new ListItem( "A Mouse" ) );
this.Items.Add( new ListItem( "A Frog" ) { Disabled = true } );
//-- Just select the first item
this.SelectedItem = this.Items[0];
}
}
主页:
public MainPage()
{
//-- Define the DataContext BEFORE the UI will be initialized ;)
this.DataContext = new MainPageViewModel();
InitializeComponent();
//-- Never saw such code before -> just don't do that ;)
//this.WhenActivated( d =>
//{
// this.OneWayBind( ViewModel, vm => vm.Items, v => v.MyComboBox.ItemsSource )
// .DisposeWith( d );
// this.Bind( ViewModel, vm => vm.SelectedItem, v => v.MyComboBox.SelectedItem )
// .DisposeWith( d );
//} );
}
Xaml 标记:
<DockPanel>
<ComboBox
DockPanel.Dock="Top"
Name="MyComboBox"
Margin="0,0,0,20"
Foreground="black"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Disabled}" Value="True">
<Setter Property="Foreground" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<!-- Details View -->
<StackPanel>
<!-- name -->
<StackPanel Orientation="Horizontal">
<Label Content="Item Name" />
<TextBox Text="{Binding SelectedItem.Text}" />
</StackPanel>
<!-- disabled flag -->
<StackPanel Orientation="Horizontal">
<Label Content="IsDisabled" />
<CheckBox IsChecked="{Binding SelectedItem.Disabled}" />
</StackPanel>
</StackPanel>
</DockPanel>
我希望这能满足您的要求。玩得开心:)