根据项目属性设置 WPF ListViewItem 的格式。

huangapple go评论72阅读模式
英文:

Set WPF ListViewItem format based on item properties

问题

以下是您提供的内容的中文翻译:

虽然有许多与ListBox一起执行此操作的帖子,但ListView控件似乎相当难处理。

简而言之,我想要在ListView中突出显示某些行为“已禁用”,但仍允许用户搜索/查看它们。

在注释掉的XAML部分中,我可以很好地明确设置所有ListViewItemBackground。然而,如果我尝试进行绑定,它会失败……尽管它不会给出任何警告并且运行得很好——它只是不起作用。

我可以明确地为每一列创建一个TextBlock并在那里设置Background,但这将导致相当多的复制,而我正试图避免这种情况,因为在我的实际示例中有相当多的列。

我尝试设置DataTemplate,但无法弄清楚如何允许每列发生绑定——我看到的所有示例都是专门绑定到项目属性(在我的情况下是一列)。我需要这样一个模板来为所有列设置样式,但在其他地方指定列内容。

我认为我接近了解决方案,但我在某个地方漏掉了一小部分或者有些东西没有正确连接。

为了简洁起见,要复制的代码如下:

XAML

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200">
  <ListView Margin="5" VerticalAlignment="Stretch" 
              SelectionMode="Single" ItemsSource="{Binding Path=Items}"
              SelectedItem="{Binding SelectedItem,Mode=TwoWay}">
    <ListView.Resources>
      <Style TargetType="{x:Type ListViewItem}">
        <!--<Setter Property="Background" Value="{Binding Background}" />-->
        <!--<Setter Property="Background" Value="Blue" />-->
      </Style>
      <!--<DataTemplate DataType="{x:Type local:FooItemViewModel}">
        <TextBlock Background="{Binding Background}"></TextBlock>
      </DataTemplate>-->
    </ListView.Resources>
    <ListView.View>
      <GridView AllowsColumnReorder="True">
        <GridView.ColumnHeaderContainerStyle>
          <Style>
            <Setter Property="GridViewColumnHeader.HorizontalContentAlignment" Value="Left" />
            <Setter Property="GridViewColumnHeader.Padding" Value="10 0" />
          </Style>
        </GridView.ColumnHeaderContainerStyle>
        <GridViewColumn Header="ID" Width="Auto" DisplayMemberBinding="{Binding Id}" />
        <GridViewColumn Header="Barcode" Width="Auto" DisplayMemberBinding="{Binding Barcode}" />
        <GridViewColumn Header="Check" Width="Auto">
          <GridViewColumn.CellTemplate>
            <DataTemplate>
              <CheckBox IsChecked="{Binding Selected}"/>
            </DataTemplate>
          </GridViewColumn.CellTemplate>
        </GridViewColumn>
      </GridView>
    </ListView.View>
  </ListView>
</Window>

XAML的后台代码

using System.Collections.ObjectModel;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        private FooViewModel _viewModel;

        public MainWindow()
        {
            InitializeComponent();
            _viewModel = new FooViewModel();
            DataContext = _viewModel;
            Loaded += MainWindow_Loaded;
        }

        public void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            _viewModel.Items = new ObservableCollection<FooItemViewModel>
            {
                new FooItemViewModel { Id = 1, Barcode = "test1"},
                new FooItemViewModel { Id = 2, Barcode = "test2", Selected = true},
                new FooItemViewModel { Id = 3, Barcode = "test3"},
            };
        }
    }
}

视图模型

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;

namespace WpfApp1
{
    internal class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void UpdateProperty<T>(ref T toSet, T value, [CallerMemberName] string propertyName = null)
        {
            if ((toSet == null && value != null) || !toSet.Equals(value))
            {
                toSet = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    internal class FooItemViewModel : BaseViewModel
    {
        private int _id;
        private string _barcode;
        private bool _selected;

        public int Id
        {
            get => _id; 
            set => UpdateProperty(ref _id, value);
        }

        public string Barcode
        {
            get => _barcode; 
            set => UpdateProperty(ref _barcode, value);
        }

        public bool Selected
        {
            get => _selected; 
            set => UpdateProperty(ref _selected, value);
        }

        public Color Background => Color.Blue;
    }

    internal class FooViewModel : BaseViewModel
    {
        private FooItemViewModel _selectedItem;
        private ObservableCollection<FooItemViewModel> _items = new ObservableCollection<FooItemViewModel>();

        public FooItemViewModel Selecteditem
        {
            get => (FooItemViewModel)_selectedItem; 
            set => UpdateProperty(ref _selectedItem, value);
        }

        public ObservableCollection<FooItemViewModel> Items
        {
            get => _items; 
            set => UpdateProperty(ref _items, value);
        }
    }
}

希望这可以帮助您理解代码和问题。

英文:

While there are many posts relating to doing this with a ListBox, the ListView control is proving quite resistant.

In a nutshell, I'd like to highlight certain rows in the ListView as 'disabled' but still allow the user to search/see them listed.

In the commented out XAML sections, I can explicitly set the Background for all ListViewItems just fine. However, if I try to bind it, it fails...though it doesn't give any warnings and runs just fine - it simply doesn't work.

I could explicitly create a TextBlock for each column and set the Background there but that would result in quite a bit of copypasta which I'm looking to avoid as I've quite a few columns in my real-world example.

I've tried setting a DataTemplate but can't figure how to allow the bindings to happen per column - all the examples I've seen bind specifically to the item property (in my case, a column). I'd need such a template to set the style for all columns, but specify the column content elsewhere.

I think I'm close but I'm missing some small piece somewhere or have something not wired up correctly.

For berivity, the code to reproduce is as follows;

XAML

&lt;Window x:Class=&quot;WpfApp1.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;
        xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
        xmlns:local=&quot;clr-namespace:WpfApp1&quot;
        mc:Ignorable=&quot;d&quot;
        Title=&quot;MainWindow&quot; Height=&quot;200&quot; Width=&quot;200&quot;&gt;
  &lt;ListView Margin=&quot;5&quot; VerticalAlignment=&quot;Stretch&quot; 
              SelectionMode=&quot;Single&quot; ItemsSource=&quot;{Binding Path=Items}&quot;
              SelectedItem=&quot;{Binding SelectedItem,Mode=TwoWay}&quot;&gt;
    &lt;ListView.Resources&gt;
      &lt;Style TargetType=&quot;{x:Type ListViewItem}&quot;&gt;
        &lt;!--&lt;Setter Property=&quot;Background&quot; Value=&quot;{Binding Background}&quot; /&gt;--&gt;
        &lt;!--&lt;Setter Property=&quot;Background&quot; Value=&quot;Blue&quot; /&gt;--&gt;
      &lt;/Style&gt;
      &lt;!--&lt;DataTemplate DataType=&quot;{x:Type local:FooItemViewModel}&quot;&gt;
        &lt;TextBlock Background=&quot;{Binding Background}&quot;&gt;&lt;/TextBlock&gt;
      &lt;/DataTemplate&gt;--&gt;
    &lt;/ListView.Resources&gt;
    &lt;ListView.View&gt;
      &lt;GridView AllowsColumnReorder=&quot;True&quot;&gt;
        &lt;GridView.ColumnHeaderContainerStyle&gt;
          &lt;Style&gt;
            &lt;Setter Property=&quot;GridViewColumnHeader.HorizontalContentAlignment&quot; Value=&quot;Left&quot; /&gt;
            &lt;Setter Property=&quot;GridViewColumnHeader.Padding&quot; Value=&quot;10 0&quot; /&gt;
          &lt;/Style&gt;
        &lt;/GridView.ColumnHeaderContainerStyle&gt;
        &lt;GridViewColumn Header=&quot;ID&quot; Width=&quot;Auto&quot; DisplayMemberBinding=&quot;{Binding Id}&quot; /&gt;
        &lt;GridViewColumn Header=&quot;Barcode&quot; Width=&quot;Auto&quot; DisplayMemberBinding=&quot;{Binding Barcode}&quot; /&gt;
        &lt;GridViewColumn Header=&quot;Check&quot; Width=&quot;Auto&quot;&gt;
          &lt;GridViewColumn.CellTemplate&gt;
            &lt;DataTemplate&gt;
              &lt;CheckBox IsChecked=&quot;{Binding Selected}&quot;/&gt;
            &lt;/DataTemplate&gt;
          &lt;/GridViewColumn.CellTemplate&gt;
        &lt;/GridViewColumn&gt;
      &lt;/GridView&gt;
    &lt;/ListView.View&gt;
  &lt;/ListView&gt;
&lt;/Window&gt;

Code behind for XAML

using System.Collections.ObjectModel;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        private FooViewModel _viewModel;

        public MainWindow()
        {
            InitializeComponent();
            _viewModel = new FooViewModel();
            DataContext = _viewModel;
            Loaded += MainWindow_Loaded;
        }

        public void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            _viewModel.Items = new ObservableCollection&lt;FooItemViewModel&gt;
            {
                new FooItemViewModel { Id = 1, Barcode = &quot;test1&quot;},
                new FooItemViewModel { Id = 2, Barcode = &quot;test2&quot;, Selected = true},
                new FooItemViewModel { Id = 3, Barcode = &quot;test3&quot;},
            };
        }
    }
}

View Models

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;

namespace WpfApp1
{
    internal class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void UpdateProperty&lt;T&gt;(ref T toSet, T value, [CallerMemberName] string propertyName = null)
        {
            if ((toSet == null &amp;&amp; value != null) || !toSet.Equals(value))
            {
                toSet = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    internal class FooItemViewModel : BaseViewModel
    {
        private int _id;
        private string _barcode;
        private bool _selected;

        public int Id
        {
            get =&gt; _id; 
            set =&gt; UpdateProperty(ref _id, value);
        }

        public string Barcode
        {
            get =&gt; _barcode; 
            set =&gt; UpdateProperty(ref _barcode, value);
        }

        public bool Selected
        {
            get =&gt; _selected; 
            set =&gt; UpdateProperty(ref _selected, value);
        }

        public Color Background =&gt; Color.Blue;
    }

    internal class FooViewModel : BaseViewModel
    {
        private FooItemViewModel _selectedItem;
        private ObservableCollection&lt;FooItemViewModel&gt; _items = new ObservableCollection&lt;FooItemViewModel&gt;();

        public FooItemViewModel Selecteditem
        {
            get =&gt; (FooItemViewModel)_selectedItem; 
            set =&gt; UpdateProperty(ref _selectedItem, value);
        }

        public ObservableCollection&lt;FooItemViewModel&gt; Items
        {
            get =&gt; _items; 
            set =&gt; UpdateProperty(ref _items, value);
        }
    }
}

答案1

得分: 1

The Background property should return a Brush instead of a Color for the binding to work:

public Brush Background =&gt; Brushes.Blue;
英文:

The Background property should should return a Brush instead of a Color for the binding to work:

public Brush Background =&gt; Brushes.Blue;

huangapple
  • 本文由 发表于 2023年5月25日 19:46:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76331919.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定