【问题标题】:WPF MVVM Chart change axesWPF MVVM 图表更改轴
【发布时间】:2010-06-11 17:07:16
【问题描述】:

我是 WPF 和 MVVM 的新手。我正在努力确定更改图表视图的最佳方法。也就是说,最初图表可能具有轴:X - ID、Y - 长度,然后在用户更改视图(通过 lisbox、单选按钮等)后,图表将显示信息:X - 长度、Y - ID ,并且在用户进行第三次更改后,它可能会显示新内容:X - ID,Y - 质量。

我最初的想法是,最好的方法是更改​​绑定本身。但我不知道如何告诉 XAML 中的控件使用 ViewModel 中的 Binding 对象进行绑定,或者在运行时更改该绑定是否安全?

然后我想也许我可以拥有一个具有成员 X 和 Y 的通用模型,并根据需要在视图模型中填充它们?

我最后的想法是,我可以有 3 个不同的图表控件,并根据需要隐藏和显示它们。

在 MVVM 模式中执行此操作的正确/建议方法是什么?任何代码示例将不胜感激。

谢谢

这是绑定到绑定方法的内容:

XAML:

        <charting:Chart.Series>
            <charting:BubbleSeries Name="bubbleSeries1"
                                   ClipToBounds="False"
                                   model:MakeDependencyProperty.IndependentValueBinding="{Binding AxisChoice.XBinding}"
                                   model:MakeDependencyProperty.DependentValueBinding="{Binding AxisChoice.YBinding}"
                                   model:MakeDependencyProperty.SizeValueBinding="{Binding AxisChoice.SizeBinding}"
                                   IsSelectionEnabled="True" SelectionChanged="bubbleSeries1_SelectionChanged"
                                   ItemsSource="{Binding Data}">
            </charting:BubbleSeries>
        </charting:Chart.Series>

        <ComboBox Height="100" Name="listBox1" Width="120" SelectedItem="{Binding AxisChoice}">
            <model:AxisGroup XBinding="{Binding Performance}" YBinding="{Binding TotalCount}" SizeBinding="{Binding TotalCount}" Selector.IsSelected="True"/>
            <model:AxisGroup XBinding="{Binding ID}" YBinding="{Binding TotalCount}" SizeBinding="{Binding BadPerformance}"/>
            <model:AxisGroup XBinding="{Binding ID}" YBinding="{Binding BadPerformance}" SizeBinding="{Binding TotalCount}"/>
        </ComboBox>

轴组:

public class AxisGroup : DependencyObject// : FrameworkElement
{
    public Binding XBinding { get; set; }
    public Binding YBinding { get; set; }
    public Binding SizeBinding { get; set; }
}

DP:

public class MakeDependencyProperty : DependencyObject
{
    public static Binding GetIndependentValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(IndependentValueBindingProperty); }
    public static void SetIndependentValueBinding(DependencyObject obj, Binding value) { obj.SetValue(IndependentValueBindingProperty, value); }
    public static readonly DependencyProperty IndependentValueBindingProperty =
        DependencyProperty.RegisterAttached("IndependentValueBinding", typeof(Binding), typeof(MakeDependencyProperty), new PropertyMetadata { PropertyChangedCallback = (obj, e) => { ((BubbleSeries)obj).IndependentValueBinding = (Binding)e.NewValue;}});


    public static Binding GetDependentValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(DependentValueBindingProperty); }
    public static void SetDependentValueBinding(DependencyObject obj, Binding value) { obj.SetValue(DependentValueBindingProperty, value); }
    public static readonly DependencyProperty DependentValueBindingProperty =
        DependencyProperty.RegisterAttached("DependentValueBinding", typeof(Binding), typeof(MakeDependencyProperty), new PropertyMetadata { PropertyChangedCallback = (obj, e) => { ((BubbleSeries)obj).DependentValueBinding = (Binding)e.NewValue; } });

    public static Binding GetSizeValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(SizeValueBindingProperty); }
    public static void SetSizeValueBinding(DependencyObject obj, Binding value) { obj.SetValue(SizeValueBindingProperty, value); }
    public static readonly DependencyProperty SizeValueBindingProperty =
        DependencyProperty.RegisterAttached("SizeValueBinding", typeof(Binding), typeof(MakeDependencyProperty), new PropertyMetadata { PropertyChangedCallback = (obj, e) => { ((BubbleSeries)obj).SizeValueBinding = (Binding)e.NewValue; } }); 
}

视图模型:

public class BubbleViewModel : BindableObject
{
    private IEnumerable<SessionPerformanceInfo> data;
    public IEnumerable<SessionPerformanceInfo> Data { ... }

    public AxisGroup AxisChoice;
}

这会产生以下异常: + $exception {"值不能为空。\r\n参数名称:绑定"} System.Exception {System.ArgumentNullException}

与bubbleSeries 中的4 个绑定有关。我很可能在绑定路径上做错了,但正如我所说,我是绑定和 wpf 的新手,所以任何提示都将不胜感激。

【问题讨论】:

    标签: wpf mvvm binding


    【解决方案1】:

    您最初的想法是正确的:您可以绑定到绑定,例如,如果您想同时更改两个轴,您可能有一个这样的 ComboBox:

    <ComboBox SelectedItem="{Binding AxisChoice}">
      <my:AxisChoice XBinding="{Binding ID}" YBinding="{Binding Length}" />
      <my:AxisChoice XBinding="{Binding Length}" YBinding="{Binding ID}" />
      <my:AxisChoice XBinding="{Binding ID}" YBinding="{Binding Quality}" />
    </ComboBox>
    

    要完成这项工作,您需要将 XBinding 和 YBinding 声明为“Binding”类型的 CLR 属性:

    public class AxisChoice
    {
      public Binding XBinding { get; set; }
      public Binding YBinding { get; set; }
    }
    

    理想情况下,您可以简单地绑定图表的 DependentValueBinding 或 IndependentValueBinding:

    <Chart ...>
      <LineSeries
        DependentValueBinding="{Binding AxisChoice.XBinding}"
        IndependentValueBinding="{Binding AxisChoice.YBinding}" />
    </Chart>
    

    很遗憾,这不起作用,因为 DependentValueBinding 和 IndependentValueBinding 不是 DependencyProperties。

    解决方法是创建一个附加的 DependencyProperty 来镜像每个不是 DependencyProperty 的属性,例如:

    public class MakeDP : DependencyObject
    {
      public static Binding GetIndependentValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(IndependentValueBindingProperty); }
      public static void SetIndependentValueBinding(DependencyObject obj, Binding value) { obj.SetValue(IndependentValueBindingProperty, value); }
      public static readonly DependencyProperty IndependentValueBindingProperty = DependencyProperty.RegisterAttached("IndependentValueBinding", typeof(Binding), typeof(MakeDP), new PropertyMetadata
      {
        PropertyChangedCallback = (obj, e) =>
        {
          ((DataPointSeries)obj).IndependentValueBinding = (Binding)e.NewValue;
        }
      });
    
      public static Binding GetDependentValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(DependentValueBindingProperty); }
      public static void SetDependentValueBinding(DependencyObject obj, Binding value) { obj.SetValue(DependentValueBindingProperty, value); }
      public static readonly DependencyProperty DependentValueBindingProperty = DependencyProperty.RegisterAttached("DependentValueBinding", typeof(Binding), typeof(MakeDP), new PropertyMetadata
      {
        PropertyChangedCallback = (obj, e) =>
          {
            ((DataPointSeries)obj).DependentValueBinding = (Binding)e.NewValue;
          }
      });
    
    }
    

    所以你的 XAML 变成:

    <Chart ...>
      <LineSeries
        my:MakeDP.DependentValueBinding="{Binding AxisChoice.XBinding}"
        my:MakeDP.IndependentValueBinding="{Binding AxisChoice,YBinding}" />
    </Chart>
    

    如果您想单独更改轴(两个单独的 ComboBox 或 ListBox),则不需要 AxisChoice:只需使每个 ComboBox 的 Items 或 ItemsSource 由绑定组成,并将“XBinding”和“YBinding”属性直接在您的视图模型中。

    请注意,如果您的控件公开常规属性而不是 Binding 类型的属性,您仍然可以使用此方法,但在这种情况下,您将使用 BindingOperations.SetBinding 而不是仅存储绑定值。

    例如,如果要更改 TextBlock 中文本的绑定:

    <TextBlock Text="{Binding FirstName}" />
    

    <TextBlock Text="{Binding LastName}" />
    

    基于 ComboBox 或 ListBox 选择,您可以使用附加属性,如下所示:

    <TextBlock my:BindingHelper.TextBinding="{Binding XBinding}" />
    

    附加属性的实现很简单:

    public class BindingHelper : DependencyObject
    {
      public static BindingBase GetTextBinding(DependencyObject obj) { return (BindingBase)obj.GetValue(TextBindingProperty); }
      public static void SetTextBinding(DependencyObject obj, BindingBase value) { obj.SetValue(TextBindingProperty, value); }
      public static readonly DependencyProperty TextBindingProperty = DependencyProperty.RegisterAttached("TextBinding", typeof(BindingBase), typeof(BindingHelper), new PropertyMetadata
      {
        PropertyChangedCallback = (obj, e) =>
          BindingOperations.SetBinding(obj, TextBlock.TextProperty, (BindingBase)e.NewValue)
      });
    }
    

    【讨论】:

    • 我确实在使用 WPFToolkit,因此使用了您的那部分建议。不幸的是,我遇到了axischoice 元素的绑定问题。 > System.Windows.Data 错误:2:找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement。绑定表达式:路径=总计数;数据项=空;目标元素是“AxisGroup”(HashCode=66152109);目标属性是“SizeBinding”(类型“Binding”)我还必须让 AxisChoice 对象继承自 dependencyobject 以获取 GetValue 和 SetValue 函数。
    • 我原来的答案不起作用,因为 Binding.ProvideValue 忽略了任何 DependencyProperty 的类型。我已经更新了我的答案来解决这个问题。
    【解决方案2】:

    我试图简化事情,所以我使 ComboBox(Y1 轴)的 ItemsSource 包含一个可观察的绑定集合,我将“YBinding”属性直接放在 ViewModel 中并将公共绑定属性设置为组合框 SelectedItem。

    在使用公共绑定 SelectedY1 时,依赖值绑定使应用程序崩溃:

    <ComboBox Height="22" Name="comboBox1" 
                DisplayMemberPath="Source.MetricVarName"                           
                ItemsSource="{Binding AllY1Choices}"   
                SelectedIndex="0" 
                SelectedItem="{Binding SelectedY1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    </ComboBox>
    
    <chartingToolkit:LineSeries
            ItemsSource="{Binding AllY1Axis}"
            IndependentValueBinding="{Binding AccumDate}"                            
        my:MakeDP.DependentValueBinding="{Binding SelectedY1}">
    

    在虚拟机中:

    private Binding _Y1axisChoice = new Binding();
    private ObservableCollection<Binding> _allY1Choices = new ObservableCollection<Binding>();
    public ObservableCollection<Binding> AllY1Choices
    {
        get { return _allY1Choices; }
        set
        {
        _allY1Choices = value;
        OnPropertyChanged("AllY1Choices");
        }
    }
    
    private Binding _selectedY1 = new Binding();
    public Binding SelectedY1
    {
        get { return _selectedY1; }
        set
        {
            if (_selectedY1 != value)
            {
                _selectedY1 = value;
                OnPropertyChanged("SelectedY1");                    
            }
        }
    }
    

    在 VM 构造函数中:

    _Y1axisChoice = new Binding("MetricVarID");
    _Y1axisChoice.Source = AllY1MetricVars[0];
    _selectedY1 = _Y1axisChoice; // set default for combobox
    
    _allY1Choices.Add(_Y1axisChoice);
    _Y1axisChoice = new Binding("MetricVarID");
    _Y1axisChoice.Source = AllY1MetricVars[1];
    
    _allY1Choices.Add(_Y1axisChoice);
    

    对此有什么想法吗?绑定对象“SelectedY1”具有 Source.MetricID="OldA",这是依赖值绑定的有效值。

    错误: System.Windows.Controls.DataVisualization.Toolkit.dll 中出现“System.InvalidOperationException”类型的异常,但未在用户代码中处理

    附加信息:分配的从属轴不能使用。这可能是由于未设置轴的方向属性或正在绘制的值与轴支持的值之间的类型不匹配。

    谢谢

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-23
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 2016-01-29
      • 1970-01-01
      • 1970-01-01
      • 2012-06-23
      相关资源
      最近更新 更多