Wpf 在XAML中,如果函数是';的输入和输出绑定到不同的对象?
我想根据带有时间变量的函数(Lissagous)在画布上移动一个小椭圆。有两个滑块可以改变函数的水平和垂直振幅,函数本身应该在定时器滴答声中调用(这实际上就是问题所在)。我成功地管理了滑块值+函数参数的绑定(使用XAML中的Wpf 在XAML中,如果函数是';的输入和输出绑定到不同的对象?,wpf,xaml,binding,timer,Wpf,Xaml,Binding,Timer,我想根据带有时间变量的函数(Lissagous)在画布上移动一个小椭圆。有两个滑块可以改变函数的水平和垂直振幅,函数本身应该在定时器滴答声中调用(这实际上就是问题所在)。我成功地管理了滑块值+函数参数的绑定(使用XAML中的ObjectDataProvider标记),还使用canvas.Left和canvas.Top对函数输出+画布上椭圆的坐标进行了绑定。 问题是,据我所知,这种绑定(对于输入和输出)只有在用户更改滑块时才起作用。这意味着,如果我在计时器中调用该函数,它会在每个刻度上生成新的数字
ObjectDataProvider
标记),还使用canvas.Left
和canvas.Top
对函数输出+画布上椭圆的坐标进行了绑定。
问题是,据我所知,这种绑定(对于输入和输出)只有在用户更改滑块时才起作用。这意味着,如果我在计时器中调用该函数,它会在每个刻度上生成新的数字,但不会改变椭圆在画布上的位置。但是,如果用户移动滑块,椭圆将相应地在画布上移动,而与t的变化无关。我是不是期望太高了?我是否以某种方式限制了函数的输出?如果是这样,我应该怎么做才能调用函数来移动椭圆,无论滑块是否被更改。
以下是我的XAML代码的大部分:
<Window.Resources>
<ObjectDataProvider x:Key="lissagousX"
ObjectType="{x:Type LissagousXAML:Lissagous}" MethodName="LissFuncX">
<ObjectDataProvider.MethodParameters>
<system:Double>1</system:Double>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="lissagousY"
ObjectType="{x:Type LissagousXAML:Lissagous}" MethodName="LissFuncY">
<ObjectDataProvider.MethodParameters>
<system:Double>1</system:Double>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
已编辑:我只需要使用纯绑定而不使用事件处理程序来管理此概念。如果将UI绑定到视图模型中的属性,会更容易,如下所示。变量名与维基百科文章中使用的变量名相对应 现在,您可以像这样绑定到视图模型属性:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Canvas Margin="200">
<Ellipse Fill="Black" Height="10" Width="10"
Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}" />
</Canvas>
<Slider Grid.Row="1" Orientation="Horizontal" Width="100"
Minimum="50" Maximum="200" Value="{Binding A}"/>
<Slider Grid.Column="1" Orientation="Vertical" Height="100"
Minimum="50" Maximum="200" Value="{Binding B}"/>
</Grid>
public MainWindow()
{
InitializeComponent();
var viewModel = new LissajousViewModel
{
a = 1,
b = 1.5,
A = 100,
B = 100
};
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(20),
IsEnabled = true
};
timer.Tick += (s, e) => viewModel.Update();
DataContext = viewModel;
}
克莱门斯,你的代码工作得很好。我非常感激。但如果你能告诉我我的错误,告诉我为什么它不起作用,或者告诉我如何改变它,我将不胜感激。这样我会学到更多!我刚刚编辑了我的代码。很抱歉,我没有强调这一点,我需要使用绑定而不是处理程序来完成:(核心思想是数据绑定与属性一起工作(与方法不太好),并且有一个通用的体系结构模式,称为MVVM,它可以帮助您分离应用程序的各个层。
public double LissFuncX(double A)
public double LissFuncY(double B)
public class LissajousViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private DateTime startTime = DateTime.Now;
public double a { get; set; }
public double b { get; set; }
public double A { get; set; }
public double B { get; set; }
public double X { get; private set; }
public double Y { get; private set; }
public void Update()
{
var t = (DateTime.Now - startTime).TotalSeconds;
X = A * Math.Sin(a * t);
Y = B * Math.Cos(b * t);
RaisePropertyChanged("X");
RaisePropertyChanged("Y");
}
private void RaisePropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Canvas Margin="200">
<Ellipse Fill="Black" Height="10" Width="10"
Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}" />
</Canvas>
<Slider Grid.Row="1" Orientation="Horizontal" Width="100"
Minimum="50" Maximum="200" Value="{Binding A}"/>
<Slider Grid.Column="1" Orientation="Vertical" Height="100"
Minimum="50" Maximum="200" Value="{Binding B}"/>
</Grid>
public MainWindow()
{
InitializeComponent();
var viewModel = new LissajousViewModel
{
a = 1,
b = 1.5,
A = 100,
B = 100
};
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(20),
IsEnabled = true
};
timer.Tick += (s, e) => viewModel.Update();
DataContext = viewModel;
}