【问题标题】:WPF bind command to colorpickerWPF 绑定命令到颜色选择器
【发布时间】:2017-02-18 19:31:16
【问题描述】:

首先,在我的应用程序中,我使用的是 Extended WPF Toolkit ColorPicker。

在画布上,我根据反序列化的 XML 文件绘制了几个矩形(效果很好)。对于每个绘制的矩形,我添加了一个 ColorPicker,以便应用程序的用户可以更改矩形的颜色。我尝试实现ICommand 接口,但颜色选择器似乎不支持绑定自定义命令,所以我有点卡住了,不知道该怎么做。这是我当前的代码:

<ItemsControl Name="inputs2">
      <ItemsControl.ItemTemplate>
      <DataTemplate>
      <Grid Name="testgrid" Margin="0,0,0,5">
      <Grid.ColumnDefinitions>
           <ColumnDefinition Width="*" />
           <ColumnDefinition Width="*" />
           </Grid.ColumnDefinitions>

           <TextBlock HorizontalAlignment="Left" Grid.Column="0" Text="{Binding Title}" />
           <xctk:ColorPicker HorizontalAlignment="Left" 
                             Grid.Column="1" 
                             Name="ClrPcker_Background" 
                             SelectedColor="{Binding Color}" 

                           <!--tryig to bind here???-->
            />
       </Grid>
       </DataTemplate>
       </ItemsControl.ItemTemplate>
</ItemsControl> 

我的 C#

internal class BackgroundInput
{
    public BackgroundInput()
    {
    }

    public string Color { get; set; }
    public string Title { get; set; }
}

第二部分:

public override void Draw(Canvas label)
{
    Rectangle rect = new Rectangle();
    //...... drawing method
    label.Children.Add(rect );
}

internal override void AddInputs(ItemsControl inputPanel)
{
    inputPanel.Items.Add(new BackgroundInput() { Title = "Backgroundcolor:", Color = BackgroundColor });        
}
//both methods get called

所以每当 ColorPicker 的颜色发生变化时,我希望连接的矩形更新它的背景颜色。 ColorPicker 似乎不支持 Command="..."CommandParameter="..." 发送数据,所以我想就如何做到这一点提供建议。谢谢!

编辑

我现在拥有的一个小例子。如果用户更改颜色选择器的颜色,我会尝试更改颜色。

编辑

我有点让它工作,但我认为这不是正确的方法。这是我更新的代码:

internal class BackgroundInput
{
    private string _bg;
    public BackgroundInput()
    {
    }

    public Box Box { get; internal set; }

    public string Color
    {
        get { return _bg; }
        set { _bg = value; Box.BackgroundColor = _bg; Box.redraw(); }
    }

    public string Title { get; set; }
}

矩形代码:

public class Box : Field
{
      Canvas label;
    Border _border;

    public override void Draw(Canvas label)
    {
        _border = new Border();
        _label = label;
        Rectangle x= new Rectangle ();

        label.Children.Add(x);
    }

    internal override void AddInputs(ItemsControl inputPanel)
    {
        inputPanel.Items.Add(new BackgroundInput() { Title = "Background:", Box = this, Color = BackgroundColor });           
    }   

    public void redraw()
    {
        _label.Children.Remove(_label);
        Draw(_label);
    }
}

【问题讨论】:

  • 将 Rectangle.Background 绑定到对应的 BackgroundInput.Color。不需要做一些命令的东西。
  • @Mat 感谢您的评论!我对 WPF 很陌生(两周前开始使用它:p)我将如何绑定它?
  • 您确定要使用画布作为布局面板吗?
  • @Mat 是的。我正在制作一种“标签设计器”,其中每个子元素都根据左上角的 x 和 y 坐标定位(非常适合画布吗?)。然后将画布放置在 ViewBox 中,这样当画布调整大小时,所有元素都将保持纵横比并完美缩放(这已经有效)
  • 那么这听起来是正确的选择 ;-)

标签: c# wpf data-binding


【解决方案1】:

我强烈建议您使用 MVVM 模式。我猜您有一个要在布局面板(在这种情况下为画布)上绘制的对象列表。

首先需要的是一个 ViewModel。它将代表一个 Rectangle 实例:

public class MyRectangleViewModel : INotifyPropertyChanged
{
    private Color _background;
    public Color Background
    {
        get
        {
            return _background;
        }
        set
        {
            _background = value;
            OnPropertyChanged();
        }
    }

    /// <summary>
    /// Occurs when [property changed].
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

请注意INotifiyPropertyChanged 接口。这将添加“WPF 绑定魔法”。每次更改属性时都会触发 PropertyChanged 事件。 SO 和网络上有大量的教程和示例......

您可以使用ItemsControl 或类似名称并将您的所有项目绑定到ItemsSource。

无论如何。看起来您想在代码中创建所有元素(无论出于何种原因)。所以在这里你有一个解决方案背后的代码#ItGivesMeTheCreeps:

/// <summary>
/// All my recangles
/// </summary>
private ObservableCollection<MyRectangleViewModel> AllMyRecangles = new ObservableCollection<MyRectangleViewModel>();
/// <summary>
/// Call this method if you add/remove objects to your list.
/// </summary>
public void RefreshObjects()
{
    this.myCanvas.Children.Clear();

    foreach (MyRectangleViewModel item in AllMyRecangles)
    {
        Rectangle newRectangle = new Rectangle();

        //set the DataContext
        newRectangle.DataContext = item;

        //create the binding
        Binding b = new Binding();
        b.Source = item;
        b.Path =new PropertyPath(nameof(Background));
        b.Mode = BindingMode.TwoWay;
        b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

        //setup the binding XAML: <Rectangle Fill = {Binding Path="Background", Mode=TwoWay, UpdateSourceTrigger ="PropertyChanged" />
        BindingOperations.SetBinding(newRectangle, Rectangle.FillProperty, b);


        //add the rectangle to the canvas
        myCanvas.Children.Add(newRectangle);
    }
}

【讨论】:

  • 你这个炸弹! :D 谢谢!我在代码中做这一切的原因是因为我正在将 XML 文件中的对象反序列化为一个包含所有数据的大对象。然后我通过 foreach 绘制该对象中的每个项目。
  • 你也可以绑定到一个大对象 ;-) WPF 就是关于绑定
  • 顺便问一下,你看到我的帖子编辑了吗?您对此有何看法?
  • 如果您的源是 XML,请创建一个 XSD 来验证源,然后将您的输入 XML 反序列化为带有 propertyChanged 通知的大对象:)
猜你喜欢
  • 2011-02-19
  • 2021-05-27
  • 1970-01-01
  • 2013-02-25
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多