WPF MVVM图表更改轴

时间:2010-06-11 17:07:17

标签: wpf mvvm binding

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

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

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

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

在MVVM模式中执行此操作的CORRECT / SUGGESTED方法是什么?任何代码示例都将非常感激。

由于

以下是绑定到绑定方法的内容:

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>

AxisGroup:

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 {“Value不能为null。\ r \ nParameter name:binding”} System.Exception {System.ArgumentNullException}

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

2 个答案:

答案 0 :(得分:4)

您最初的想法是正确的:您可以绑定到绑定,例如,如果您想要将两个轴一起更改,您可能会有这样的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)
  });
}

答案 1 :(得分:0)

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

在使用public Binding SelectedY1时,dependentvaluebinding正在崩溃应用程序:

<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}">

在VM中:

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 contstructor中:

_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'类型的异常,但未在用户代码中处理

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

由于