我正在用c#编写一个WPF应用程序。本应用程序是使用MVVM设计的。
目前,我有一个父窗口与几个复选框。用户可以选择他们想要的任何框,然后点击"plot"Button
。一旦点击"plot",就会出现一个新的子窗口,以单个图形显示数据。
所以,如果我只选中了一个复选框,然后点击"plot",我将看到一个带有单线的图形。如果我有两个复选框,选中并点击"绘图",我将看到相同的单个图形,但它上面有两条线。
我当前的实现:
目前,我有一个名为GraphWindowView
的"视图"类。视图显然需要知道要显示哪些数据。因此,为了做到这一点,我有依赖属性GraphWindowView.Dates
和GraphWindowView.Data
,最终产生Data
(y轴)与Dates
(x轴)的图形。
问题: GraphWindowView
的当前实现显然仅限于能够绘制一组数据(即Data
与Dates
)。我想使这个(很多)更可扩展,并有一个任意数量的绘图可用取决于多少复选框被选中。我该怎么做呢?我想我需要重新考虑我对依赖属性的使用…
>>>更新
所以我做了一个GraphLine
类,它应该代表图形上的一条线。"图形"实际上是GraphWindowPresenter.xaml
类中的ChartPlotter
元素。此外,我为GraphLine
对象指定了DataType
,但这就是我所理解的。接下来的步骤是什么,如何将数据添加到图中?我如何/在哪里使GraphLine
的实例填充ChartPlotter
元素?抱歉,即使在阅读了相当多的教程之后,我对这一点还是很迷茫。感谢到目前为止所有的帮助,我真的很感激!
GraphWindowView.xaml
<Window x:Class="BMSVM_Simulator.View.GraphWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:BMSVM_Simulator.ViewModel"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
x:Name="ThisGraphWindowInstance"
Title="Plot" Height="500" Width="750"
Icon="../res/qualcomm_q_icon.ico.ico"
MinWidth="400" MinHeight="300">
<Window.DataContext>
<ViewModel:GraphWindowPresenter/>
</Window.DataContext>
<d3:ChartPlotter Name="plotter" Margin="10,10,20,10">
<d3:ChartPlotter.HorizontalAxis>
<d3:HorizontalIntegerAxis Name="dateAxis"/>
</d3:ChartPlotter.HorizontalAxis>
<d3:ChartPlotter.VerticalAxis>
<d3:VerticalIntegerAxis Name="countAxis"/>
</d3:ChartPlotter.VerticalAxis>
<d3:Header FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis}"/>
<d3:HorizontalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=xAxis}"/>
</d3:ChartPlotter>
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:GraphLine}">
<!--WHAT GOES HERE-->
</DataTemplate>
</Window.Resources>
</Window>
GraphLine.cs
namespace BMSVM_Simulator.ViewModel
{
class GraphLine
{
public string xAxis { get; private set; }
public string yAxis { get; private set; }
public string title { get; private set; }
public string legend { get; private set; }
public EnumerableDataSource<int> data { get; private set; }
public EnumerableDataSource<int> dates { get; private set; }
}
}
WPF中的大多数此类问题都可以通过仔细使用数据绑定和DataTemplate
来解决,而不是使用大量的过程代码。一般的想法是,您创建一个自定义类,其中包含绘制所有线条所需的所有属性。然后,您将声明一个DataTemplate
来定义如何将各种属性绑定到数据上,可能像这样:
<DataTemplate DataType="{x:Type YourXamlNamespacePrefix:GraphLine}">
<Line X1="{Binding X1}" Y1="{Binding Y1}" X2="{Binding X2}" Y2="{Binding Y2}" />
</DataTemplate>
然后创建一个自定义类实例的集合,并将其数据绑定到一些集合控件,如ItemsControl
,每个集合将自动呈现在正确的位置:
<ItemsControl ItemsSource="{Binding YourGraphLineCollection, RelativeSource={
RelativeSource AncestorType={x:Type YourXamlNamespacePrefix:YourControlName}}}" />
欢迎来到WPF数据绑定和DataTemplate
的强大世界。
更新>>>
数据绑定到Line
元素的自定义类不是视图模型。可以把它看作一个数据类型类,您将像上面那样为它声明一个DataTemplate
。当我说它应该具有所有必需的属性时,如果您查看上面的示例,就会发现它至少需要四个double
属性来数据绑定到Line
元素的四个已使用的属性。但是,您也可以选择向数据绑定添加更多属性,例如Stroke
、StrokeThickness
或Fill
属性。
至于应该在哪里定义DataTemplate
,它应该在应用它的项的范围内。如果您想在一个视图中使用它,那么将它放在该视图的UserControl.Resources
部分中。但是,如果您想使用相同的DataTemplate
,那么您应该将其放入App.xaml
文件的Application.Resources
部分,因为这些Resources
在应用程序范围内可用。
FINAL UPDATE>>
正如我在评论中提到的,教用户如何使用WPF绝对超出了这个网站的范围,所以我不会这样做。要了解DataTemplate
s,您应该阅读MSDN上的数据模板概述页面。当你不知道一些事情的时候,MSDN应该总是是你搜索答案的第一个地方。
DependencyProperty
应该是ObservableCollection<GraphLine>
类型。在控件内部,您应该将它们数据绑定到某种ItemsControl
,如上所示-我更改了其中的Binding Path
,因为您应该真正使用RelativeSource Binding
来定位您的情况下的属性(其中YourControlName
是您想要绘制Line
对象的UserControl
的名称)。
最后,在视图模型中(与包含新UserControl
的视图链接),您将需要一个集合属性来与UserControl
中的集合进行数据绑定,比如命名为YourGraphLineCollectionInViewModel
:
<YourXamlNamespacePrefix:YourControlName YourGraphLineCollection="{Binding
YourGraphLineCollectionInViewModel}" />
在这个视图模型中,您将GraphLine
类的实例添加到YourGraphLineCollectionInViewModel
集合中,只要您已经设置了Binding Path
,它们就会出现在ItemsControl
的UI中。我假设你知道如何正确设置你的DataContext
-如果不知道,你可以很容易地在网上找到如何做到这一点。