【问题标题】:ComboBox not showing the value SelectedItem is bound toComboBox 未显示 SelectedItem 绑定到的值
【发布时间】:2020-01-24 14:13:48
【问题描述】:

我正在尝试使组合框与属性保持同步:

<ComboBox SelectedItem="{Binding StrokeSwatch}"...

这项工作除了组合框一直为空(如果框打开,则项目在此处,但没有当前/选定的项目),直到我手动选择一个值。

它应该显示红色样本和名称:


我找不到原因:SelectedItem 绑定到 (StrokeSwatch) 的属性有一个值,该值被该行使用,但组合框对这个值没有反应。

正在学习 WPF,希望对理解有所帮助。

代码...

    <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="250" Width="300">

        <Window.DataContext>
            <local:ViewModel/>
        </Window.DataContext>

        <StackPanel Margin="10">
            <StackPanel Orientation="Horizontal" Margin="10">
                <TextBlock Text="Stroke:"/>
                <ComboBox Margin="10,0,0,0" ItemsSource="{Binding SwatchesByName}" SelectedItem="{Binding StrokeSwatch}">
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Rectangle Width="25" Fill="{Binding Brush}"/>
                                <TextBlock Margin="10,0,0,0" Text="{Binding Name}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                </ComboBox>
            </StackPanel>

            <Line Margin="10" X1="0" Y1="0" X2="200" Y2="100"
                  Stroke="{Binding StrokeSwatch.Brush}"
                  StrokeThickness="2"/>
        </StackPanel>
    </Window>

C#:

    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Media;

    namespace WpfApp1 {
        public partial class MainWindow : Window {
            public MainWindow () {
                InitializeComponent ();
            }
        }

        public class ViewModel : INotifyPropertyChanged {
            Swatch strokeSwatch;
            public IEnumerable<Swatch> SwatchesByName { get => Swatches.ByName; }
            public Swatch StrokeSwatch { get => strokeSwatch; set { strokeSwatch = value; RaisePropertyChanged (); } }
            public event PropertyChangedEventHandler PropertyChanged;

            public ViewModel () {
                StrokeSwatch = Swatches.ColorToSwatch (Colors.Red);
            }

            void RaisePropertyChanged ([CallerMemberName] string propertyName = null) {
                PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName));
            }
        }

        static class Swatches {
            public static IEnumerable<Swatch> ByName { get; }

            static Swatches () {
                ByName = from PropertyInfo pi in typeof (Colors).GetProperties ()
                         orderby pi.Name
                         select new Swatch (pi.Name, (Color) pi.GetValue (null, null));
            }

            public static Swatch ColorToSwatch (Color color) {
                return ByName.First (sw => sw.Color == color);
            }
        }

        public class Swatch {
            SolidColorBrush brush;
            public string Name { get; }
            public Color Color { get; }
            public SolidColorBrush Brush { get { if (brush == null) brush = new SolidColorBrush (Color); return brush; } }

            public Swatch (string name, Color color) {
                Name = name;
                Color = color;
            }
        }
    }

【问题讨论】:

  • 确保StrokeSwatch 持有的实例实际上包含在SwatchesByName 集合中。如果您不能保证,请覆盖 Swatch 类的 Equals 方法。
  • 或者设置SelectedValuePath="Name"SelectedValue="{Binding StrokeSwatch.Name}"(而不是绑定SelectedItem)。
  • 您也可以在静态 Swatches 构造函数中通过ByName = ByName.ToList(); 强制进行初始枚举。
  • LINQ 表达式总是延迟执行。仅调用 ToList()ToArray()ToDictionary() 或任何返回单个值的方法,例如Count()First()Max() 或调用IEnumerable.GetEnumerator(例如foreach)将执行查询表达式。
  • 这里的重点是ByName.First(sw =&gt; sw.Color == color) 独立于 ComboBox 的枚举枚举表达式。因此,您创建了两组不同的 Swatch 实例,因此 SelectedItem 的相等性检查失败。

标签: wpf data-binding combobox


【解决方案1】:

IEnumerable&lt;Swatch&gt; 上调用ToArray() 以实现ByName 集合:

static Swatches()
{
    ByName = (from PropertyInfo pi in typeof(Colors).GetProperties()
              orderby pi.Name
              select new Swatch(pi.Name, (Color)pi.GetValue(null, null))).ToArray();
}

【讨论】:

  • 对了,您介意提供一个简短的解释吗?
  • 当框架调用 getter 并执行实际选择时,StrokeSwatch 返回的 Swatch 必须存在于 ItemsSource 中。如果您从ByName 返回一个序列而不是实际集合,则不会。
【解决方案2】:

首先你应该在你的 Swatche 类中使用 observableCollection 属性并返回 List

public class ViewModel : INotifyPropertyChanged
{
    Swatch strokeSwatch;

    public ObservableCollection<Swatch> SwatchesByName
    {
        get => _swatchesByName;
        set { _swatchesByName = value; RaisePropertyChanged();}
    }

    private ObservableCollection<Swatch> _swatchesByName;

    public Swatch StrokeSwatch 
    { 
        get => strokeSwatch; 
        set { strokeSwatch = value; RaisePropertyChanged(); }
    }
    public event PropertyChangedEventHandler PropertyChanged;

    public ViewModel()
    {
        SwatchesByName = new ObservableCollection<Swatch>(Swatches.ByName);
        StrokeSwatch = Swatches.ColorToSwatch(Colors.Red);
    }

    void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

static class Swatches
{
    public static List<Swatch> ByName { get; }

    static Swatches()
    {
        var list = from PropertyInfo pi in typeof(Colors).GetProperties()
            orderby pi.Name
            select new Swatch(pi.Name, (Color)pi.GetValue(null, null));
        ByName = list.ToList();
    }

    public static Swatch ColorToSwatch(Color color)
    {
        return ByName.First(sw => sw.Color == color);
    }
}

public class Swatch
{
    SolidColorBrush brush;
    public string Name { get; }
    public Color Color { get; }

    public SolidColorBrush Brush
    {
        get { if (brush == null) brush = new SolidColorBrush(Color); return brush; }
    }

    public Swatch(string name, Color color)
    {
        Name = name;
        Color = color;
    }
}

【讨论】:

  • ObservableCollection 仅在您需要在集合被修改时收到通知时才需要。
猜你喜欢
  • 1970-01-01
  • 2013-09-19
  • 2015-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-24
  • 2019-11-10
  • 2013-05-17
相关资源
最近更新 更多