我是WPF和xaml的新手,我正在制作视频播放器。我目前正在尝试将电影时间滑块绑定到当前经过的时间,该时间存储在TimeSpan
变量中,该变量每秒更新一次DispatcherTimer
。
这是我的xaml:
<customControls:ThumbDragSlider x:Name="sMovieSkipSlider" Height="25" Margin="65,0,65,71" VerticalAlignment="Bottom"
Value="{Binding ElementName=_movieElapsedTime, Path = TotalSeconds, Mode=OneWay}"
DragStarted="SMovieSkipSlider_OnDragStarted"
DragCompleted="SMovieSkipSlider_OnDragCompleted"/>
这是变量的声明方式:
private TimeSpan _movieElapsedTime;
这就是我得到的错误:
System.Windows.Data错误:4:找不到引用'ElementName = _movieElapsedTime'的绑定源。 BindingExpression:路径= TotalSeconds;的DataItem = NULL; target元素是'ThumbDragSlider'(Name ='sMovieSkipSlider'); target属性为'Value'(类型'Double')
答案 0 :(得分:1)
ElementName
用于指代XAML中的元素。如果您有控件,例如......
<TextBox x:Name="_movieElapsedTime" />
然后,如果碰巧有一个名为TotalSeconds
的属性,它就会有意义。
您也无法绑定到某个字段;它必须是带有get和可能的set的常规C#属性,或者是一种称为依赖属性的特殊属性。
但是让我们用viewmodel来做这个。 viewmodel是实现INotifyPropertyChanged
的任何随机类,并在其属性值更改时引发PropertyChanged
事件。它跟踪您的应用程序的内部。它不知道存在用户界面。它公开了像MovieElapsedTime这样的数据属性,并且可以公开命令,允许按钮或菜单项将命令发送到视图模型。它也可能具有其父视图模型可能调用的方法。
我们将编写一个实现INotifyPropertyChanged
的viewmodel基类,并从中派生一个简单的视图模型,表示视频播放器需要知道的事物。然后我们将在XAML中创建一个允许用户与之交互的UI。
您可能希望viewmodel具有启动和停止视频的命令,依此类推。这很容易在谷歌上找到。我建议使用RelayCommand / DelegateCommand类;谷歌那些,你会看到他们做了什么。有很多例子你可以窃取代码。
#region ViewModelBase Class
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
#endregion INotifyPropertyChanged
}
#endregion ViewModelBase Class
#region VideopPlayerViewModel Class
public class VideopPlayerViewModel : ViewModelBase
{
#region MovieElapsedTime Property
private TimeSpan _movieElapsedTime = default(TimeSpan);
public TimeSpan MovieElapsedTime
{
get { return _movieElapsedTime; }
set
{
if (value != _movieElapsedTime)
{
_movieElapsedTime = value;
OnPropertyChanged();
}
}
}
#endregion MovieElapsedTime Property
}
#endregion VideopPlayerViewModel Class
MainWindow构造函数:
public MainWindow()
{
InitializeComponent();
DataContext = new VideoPlayerViewModel();
}
XAML。因为A VideoPlayerViewModel
是我们窗口的DataContext,这意味着当您告诉绑定绑定到MovieElapsedTime
时,没有关于在哪里找到它的更多信息,它将转到{{1}它从窗口继承的对象。
DataContext
这是依赖属性版本。这不是“正确的做法”,但并不是完全可怕的。
下一个问题:MovieElapsedTime是什么成员?窗户?什么是DataContext?如果您在窗口上设置<customControls:ThumbDragSlider
Value="{Binding MovieElapsedTime.TotalSeconds, Mode=OneWay}"
x:Name="sMovieSkipSlider"
Height="25"
Margin="65,0,65,71"
VerticalAlignment="Bottom"
DragStarted="SMovieSkipSlider_OnDragStarted"
DragCompleted="SMovieSkipSlider_OnDragCompleted"/>
和实施DataContext = this
,那么和会在INotifyPropertyChanged
更改时提升PropertyChanged
,那就是出于其他原因的坏主意,但您的绑定将与MovieElapsedTime
一起用作传统属性。如果没有,你需要这个:
MovieElapsedTime
窗口代码隐藏:
<customControls:ThumbDragSlider
Value="{Binding MovieElapsedTime.TotalSeconds, Mode=OneWay, RelativeSource={RelativeSource AncestorType=Window}}"
x:Name="sMovieSkipSlider"
Height="25"
Margin="65,0,65,71"
VerticalAlignment="Bottom"
DragStarted="SMovieSkipSlider_OnDragStarted"
DragCompleted="SMovieSkipSlider_OnDragCompleted"/>
以那种方式定义属性而不是你拥有的属性。使用所有这些功能,当您将属性设置为新值时,控件将自动接收通知。
你应该真正编写一个实现 public TimeSpan MovieElapsedTime
{
get { return (TimeSpan)GetValue(MovieElapsedTimeProperty); }
set { SetValue(MovieElapsedTimeProperty, value); }
}
public static readonly DependencyProperty MovieElapsedTimeProperty =
DependencyProperty.Register(nameof(MovieElapsedTime), typeof(TimeSpan), typeof(MainWindow),
new PropertyMetadata(null));
的viewmodel,并使其成为viewmodel的一个属性。如果你有兴趣,我们可以通过。
但是,如果您希望更新顺利,我认为您需要每秒轮询一次以上。更有可能每500毫秒甚至250.尝试500,看看它的外观。