ElementName
is used to refer to an element in the XAML. If you had a control such as...
<TextBox x:Name="_movieElapsedTime" />
Then it would make sense the way you have it -- if it happened to have a property named TotalSeconds
.
You also can't bind to a field; it has to be either a regular C# property with a get and maybe a set, or else a special kind of property called a dependency property.
But let's do this with a viewmodel. A viewmodel is any random class that implements INotifyPropertyChanged
, and raises the PropertyChanged
event when its property values change. It keeps track of the internals of your application. It isn't aware that a user interface exists. It exposes data properties like MovieElapsedTime, and may expose commands as well which allow buttons or menu items to send orders to the viewmodel. It may also have methods that its parent viewmodel may call.
We'll write a viewmodel base class that implements INotifyPropertyChanged
, and derive a simple viewmodel from it that represents the things that a video player needs to know. Then we'll create a UI in XAML that lets the user interact with it.
You'll probably want the viewmodel to have commands to start and stop the video and so on. That's easy to find on Google. I'd recommend using a RelayCommand/DelegateCommand class; google those and you'll see what they do. There are a lot of examples out there you can steal the code for.
#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 constructor:
public MainWindow()
{
InitializeComponent();
DataContext = new VideoPlayerViewModel();
}
XAML. Because A VideoPlayerViewModel
is the DataContext for our window, that means that when you tell a binding to bind to MovieElapsedTime
, with no further information about where to find it, it will go to the DataContext
object it inherited from the window.
<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"/>
Non-MVVM version
Here's the dependency property version. It's not "the right way to do it" but it's not totally awful.
Next question: What is MovieElapsedTime a member of? The Window? What is the DataContext? If you set DataContext = this
and implemented INotifyPropertyChanged
on your window, and raise PropertyChanged
when MovieElapsedTime
changes, that's a bad idea for other reasons, but your binding will work with MovieElapsedTime
as a conventional property. If not, you need this:
<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"/>
Window codebehind:
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));
Define the property that way instead of what you have. With all that stuff, the control will receive notifications automatically when you set the property to a new value.
You should really write a viewmodel which implements INotifyPropertyChanged
and make this a property of the viewmodel. We can go through that if you're interested.
I think you'll need to poll more than once a second, though, if you want the update to be smooth. More likely every 500 ms or even 250. Try 500 and see how it looks.