C# LiveCharts系列用户控件和视图模型之间的数据绑定
我在用户控件(chart UC)中有一个图表,这就像LiveCharts缩放和平移示例一样,因此图表UC绑定到它的代码后面。我还有一个用户控件(我们称之为Item UC),它包含两个图表UC。项目UC具有绑定和命令,所有这些都与它的视图模型(项目VM)一起工作。我还没有弄清楚的是,如何在图表UCs的Series属性和项目VM之间建立连接 图表UC xaml:C# LiveCharts系列用户控件和视图模型之间的数据绑定,c#,wpf,livecharts,C#,Wpf,Livecharts,我在用户控件(chart UC)中有一个图表,这就像LiveCharts缩放和平移示例一样,因此图表UC绑定到它的代码后面。我还有一个用户控件(我们称之为Item UC),它包含两个图表UC。项目UC具有绑定和命令,所有这些都与它的视图模型(项目VM)一起工作。我还没有弄清楚的是,如何在图表UCs的Series属性和项目VM之间建立连接 图表UC xaml: <UserControl x:Class="responsive_ui_test.User_Controls.ZoomingAndP
<UserControl x:Class="responsive_ui_test.User_Controls.ZoomingAndPanning"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:responsive_ui_test.User_Controls"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
xmlns:zoomingAndPanning="clr-namespace:responsive_ui_test.User_Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<zoomingAndPanning:ZoomingModeCoverter x:Key="ZoomingModeCoverter"></zoomingAndPanning:ZoomingModeCoverter>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<lvc:CartesianChart Series="{Binding Series}" Zoom="{Binding ZoomingMode}" >
<lvc:CartesianChart.AxisX>
<lvc:Axis Name="X" Title="{Binding XAxisTitle}" LabelFormatter="{Binding XFormatter}"
Separator="{x:Static lvc:DefaultAxes.CleanSeparator}"/>
</lvc:CartesianChart.AxisX>
<lvc:CartesianChart.AxisY>
<lvc:Axis Name="Y" Title="{Binding YAxisTitle}" LabelFormatter="{Binding YFormatter}"/>
</lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
<Button Grid.Row="1" Click="ResetZoomOnClick" Background="#FFDDDDDD">Reset Zoom</Button>
</Grid>
</UserControl>
重置缩放
图表UC代码隐藏:
namespace responsive_ui_test.User_Controls
{
public partial class ZoomingAndPanning : INotifyPropertyChanged
{
private ZoomingOptions _zoomingMode;
public ZoomingAndPanning()
{
InitializeComponent();
var gradientBrush = new LinearGradientBrush
{
StartPoint = new Point(0, 0),
EndPoint = new Point(0, 1)
};
gradientBrush.GradientStops.Add(new GradientStop(Color.FromRgb(33, 148, 241), 0));
gradientBrush.GradientStops.Add(new GradientStop(Colors.Transparent, 1));
ZoomingMode = ZoomingOptions.X;
DataContext = this;
}
public SeriesCollection Series { get; set; }
public Func<double, string> XFormatter { get; set; }
public Func<double, string> YFormatter { get; set; }
public string XAxisTitle { get; set; }
public string YAxisTitle { get; set; }
public ZoomingOptions ZoomingMode
{
get { return _zoomingMode; }
set
{
_zoomingMode = value;
OnPropertyChanged();
}
}
private void ToogleZoomingMode(object sender, RoutedEventArgs e)
{
switch (ZoomingMode)
{
case ZoomingOptions.None:
ZoomingMode = ZoomingOptions.X;
break;
case ZoomingOptions.X:
ZoomingMode = ZoomingOptions.Y;
break;
case ZoomingOptions.Y:
ZoomingMode = ZoomingOptions.Xy;
break;
case ZoomingOptions.Xy:
ZoomingMode = ZoomingOptions.None;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private ChartValues<ObservablePoint> GetData()
{
var r = new Random();
var min = 0;
var max = 65535;
var values = new ChartValues<ObservablePoint>();
for (var i = 1; i < 100; i++)
{
var seed = r.NextDouble();
var value = seed * (max - min) + min;
values.Add(new ObservablePoint(i, value));
}
return values;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void ResetZoomOnClick(object sender, RoutedEventArgs e)
{
//Use the axis MinValue/MaxValue properties to specify the values to display.
//use double.Nan to clear it.
X.MinValue = double.NaN;
X.MaxValue = double.NaN;
Y.MinValue = double.NaN;
Y.MaxValue = double.NaN;
}
}
public class ZoomingModeCoverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch ((ZoomingOptions)value)
{
case ZoomingOptions.None:
return "None";
case ZoomingOptions.X:
return "X";
case ZoomingOptions.Y:
return "Y";
case ZoomingOptions.Xy:
return "XY";
default:
throw new ArgumentOutOfRangeException();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
namespace responsive_ui_test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private ChartValues<ObservablePoint> GetData()
{
var r = new Random();
var min = 0;
var max = 65535;
var values = new ChartValues<ObservablePoint>();
for (var i = 1; i < 50; i++)
{
var seed = r.NextDouble();
var value = seed * (max - min) + min;
values.Add(new ObservablePoint(i, value));
}
return values;
}
private void tbCtrl_Loaded(object sender, RoutedEventArgs e)
{
var tabControlViewModel = new TabControlViewModel();
tabControlViewModel.Items.Add(new DeviceViewModel()
{
FilePath = "C:/1/",
FoundDevices = new ObservableCollection<string>()
{
"1", "2"
},
});
DataContext = tabControlViewModel;
tbCtrl.SelectedIndex = 0;
}
private void tbCtrl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (tbCtrl.SelectedIndex < 0) // if selection is empty
return;
var cnt = tbCtrl.Items.Count;
Debug.WriteLine("Item count: " + cnt);
Debug.WriteLine("Selected Index: " + tbCtrl.SelectedIndex);
}
}
}
namespace responsive\u ui\u test.User\u控件
{
公共部分类缩放和平移:InotifyProperty已更改
{
私人缩放选项_缩放模式;
公共缩放和平移()
{
初始化组件();
var gradientBrush=新的LinearGradientBrush
{
起始点=新点(0,0),
端点=新点(0,1)
};
gradientBrush.GradientStops.Add(新的GradientStop(Color.fromgb(33148241),0));
gradientBrush.GradientStops.Add(新的GradientStop(Colors.Transparent,1));
ZoomingMode=缩放选项.X;
DataContext=this;
}
公共序列集合系列{get;set;}
公共函数XFormatter{get;set;}
公共函数YFormatter{get;set;}
公共字符串XAxisTitle{get;set;}
公共字符串YAxisTitle{get;set;}
公共缩放选项缩放模式
{
获取{return\u zoomingMode;}
设置
{
_缩放模式=值;
OnPropertyChanged();
}
}
private void ToogleZoomingMode(对象发送方、路由目标方)
{
开关(缩放模式)
{
大小写缩放选项。无:
ZoomingMode=缩放选项.X;
打破
大小写缩放选项。X:
ZoomingMode=缩放选项.Y;
打破
大小写缩放选项。Y:
ZoomingMode=缩放选项.Xy;
打破
大小写缩放选项。Xy:
ZoomingMode=缩放选项。无;
打破
违约:
抛出新ArgumentOutOfRangeException();
}
}
私有ChartValues GetData()
{
var r=新的随机变量();
var min=0;
var max=65535;
var值=新的ChartValues();
对于(变量i=1;i<100;i++)
{
var seed=r.NextDouble();
var值=种子*(最大值-最小值)+最小值;
添加(新的观察点(i,值));
}
返回值;
}
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged(字符串propertyName=null)
{
if(PropertyChanged!=null)PropertyChanged.Invoke(这是新的propertychangedventargs(propertyName));
}
private void ResetZoomOnClick(对象发送方,路由目标)
{
//使用轴MinValue/MaxValue属性指定要显示的值。
//使用double.Nan清除它。
X.MinValue=double.NaN;
X.MaxValue=double.NaN;
Y.MinValue=double.NaN;
Y.MaxValue=double.NaN;
}
}
公共类ZoomingModeConverter:IValueConverter
{
公共对象转换(对象值、类型targetType、对象参数、CultureInfo区域性)
{
开关((缩放选项)值)
{
大小写缩放选项。无:
返回“无”;
大小写缩放选项。X:
返回“X”;
大小写缩放选项。Y:
返回“Y”;
大小写缩放选项。Xy:
返回“XY”;
违约:
抛出新ArgumentOutOfRangeException();
}
}
公共对象转换回(对象值、类型targetType、对象参数、CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}
}
项目UC xaml:
<UserControl x:Class="responsive_ui_test.User_Controls.DeviceTab"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:responsive_ui_test.User_Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
xmlns:uc="clr-namespace:responsive_ui_test.User_Controls">
<Grid>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="250"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="2*" MinWidth="250"/>
</Grid.ColumnDefinitions>
<Grid Margin="0,0,5,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="80" MaxHeight="100"/>
<RowDefinition Height="*" MinHeight="80" MaxHeight="100"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<GroupBox Header="Port" Margin="0,0,0,10">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ComboBox ItemsSource="{Binding FoundDevices}" Margin="0,0,0,4"></ComboBox>
<Grid Grid.Row="1" Margin="0,4,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Command="{Binding ConnectCommand}" Margin="0,0,4,0">
Connect
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Button.Resources>
</Button>
<Button Command="{Binding ScanCommand}" Grid.Column="1" Margin="4,0,0,0">
Scan
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Button.Resources>
</Button>
</Grid>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Header="Log File" Margin="0,0,0,10">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Text="{Binding FilePath}" IsReadOnly="True" Margin="0,0,0,4"/>
<Button Command="{Binding ChangeFilePathCommand}" Grid.Row="1" Margin="0,4,0,0">
Change
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Button.Resources>
</Button>
</Grid>
</GroupBox>
<GroupBox Grid.Row="2" Header="Temperature">
<uc:ZoomingAndPanning x:Name="lvcTemp"></uc:ZoomingAndPanning>
</GroupBox>
</Grid>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<Grid Grid.Column="2" Margin="5,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="30" MaxHeight="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Command="{Binding CloseTabCommand}" Name="btnClose" Margin="0" Width="100" HorizontalAlignment="Right" Background="Red" FontWeight="Bold">
CLOSE TAB
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Button.Resources>
</Button>
<Button Command="{Binding NewTabCommand}" Name="btnNew" Margin="0,0,110,0" Width="100" HorizontalAlignment="Right" Background="LawnGreen" FontWeight="Bold">
NEW TAB
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Button.Resources>
</Button>
<GroupBox Grid.Row="1" Header="Measurement">
<uc:ZoomingAndPanning Grid.Row="1" x:Name="lvcMeas"></uc:ZoomingAndPanning>
</GroupBox>
</Grid>
</Grid>
</Grid>
</UserControl>
连接
扫描
<Window x:Class="responsive_ui_test.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:uc="clr-namespace:responsive_ui_test.User_Controls"
xmlns:local="clr-namespace:responsive_ui_test"
mc:Ignorable="d"
Title="App" Height="550" Width="800" MinWidth="800" MinHeight="550">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="CustomHeaderTemplate">
<DockPanel LastChildFill="True">
<!--<Button Content="X" DockPanel.Dock="Right">
<Button.Template>
<ControlTemplate>
<Label FontWeight="Bold" Content="X" />
</ControlTemplate>
</Button.Template>
</Button>-->
<Label Content="{Binding TabName}" />
</DockPanel>
</DataTemplate>
</Grid.Resources>
<TabControl x:Name="tbCtrl" ItemsSource="{Binding Items}" Loaded="tbCtrl_Loaded" SelectionChanged="tbCtrl_SelectionChanged" ItemTemplate="{StaticResource CustomHeaderTemplate}">
<TabControl.ContentTemplate>
<DataTemplate>
<uc:DeviceTab/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
namespace responsive_ui_test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private ChartValues<ObservablePoint> GetData()
{
var r = new Random();
var min = 0;
var max = 65535;
var values = new ChartValues<ObservablePoint>();
for (var i = 1; i < 50; i++)
{
var seed = r.NextDouble();
var value = seed * (max - min) + min;
values.Add(new ObservablePoint(i, value));
}
return values;
}
private void tbCtrl_Loaded(object sender, RoutedEventArgs e)
{
var tabControlViewModel = new TabControlViewModel();
tabControlViewModel.Items.Add(new DeviceViewModel()
{
FilePath = "C:/1/",
FoundDevices = new ObservableCollection<string>()
{
"1", "2"
},
});
DataContext = tabControlViewModel;
tbCtrl.SelectedIndex = 0;
}
private void tbCtrl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (tbCtrl.SelectedIndex < 0) // if selection is empty
return;
var cnt = tbCtrl.Items.Count;
Debug.WriteLine("Item count: " + cnt);
Debug.WriteLine("Selected Index: " + tbCtrl.SelectedIndex);
}
}
}
namespace responsive_ui_test.View_Models
{
class TabControlViewModel : ViewModelBase
{
public ObservableCollection<DeviceViewModel> Items { get; } = new ObservableCollection<DeviceViewModel>();
}
}