I am creating a custom "PageHeaderControl" UserControl, with a header property:
public partial class PageHeaderControl: UserControl
{
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header",
typeof(string), typeof(PageHeaderControl),
new PropertyMetadata(""));
public string Header
{
get { return GetValue(HeaderProperty) as string; }
set { SetValue(HeaderProperty, value); }
}
}
In the XAML for that control, I have:
<sdk:Label Content="{Binding Header,Mode=TwoWay}" />
Now for the problem: When I create the control, binding it only works to do this:
<my:PageHeaderControl Header="This is my page header" />
And it does not work to do this, where PageHeader is the property in my ViewModel holding the header value:
<my:PageHeaderControl Header="{Binding PageHeader,Mode=TwoWay}" />
I thought maybe my properties were messed up, but this also works:
<TextBlock Text="{Binding PageHeader,Mode=TwoWay}" />
Any ideas as to what the problem could be!
Thanks so much!!!
Edit:
In my ViewModel, PageHeader is this:
private string _pageHeader = "This is my page header";
public string PageHeader
{
get
{
return _pageHeader;
}
set
{
_pageHeader = value;
RaisePropertyChanged("PageHeader");
}
}
Edit 2:
When I put a breakpoint inside the "get" for my PageHeader property, it does not get hit AT ALL, unless I add in the TextBlock...
If I understand you correctly you're trying to bind a property of an element within your control's XAML markup to the property of the control itself.
If this is the case, see if the following helps you.
PageHeaderControl.xaml:
<UserControl x:Class="TryElementBinding.PageHeaderControl"
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"
x:Name = "MyControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding Header, ElementName=MyControl}"></TextBlock>
</Grid>
PageHeaderControl.xaml.cs:
public partial class PageHeaderControl : UserControl
{
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(PageHeaderControl), new PropertyMetadata(""));
public string Header
{
get
{
return GetValue(HeaderProperty) as string;
}
set
{
SetValue(HeaderProperty, value);
}
}
public PageHeaderControl()
{
InitializeComponent();
}
}
ViewModel.cs:
public class ViewModel : INotifyPropertyChanged
{
private string _pageHeader = "This is my page header";
public string PageHeader
{
get
{
return _pageHeader;
}
set
{
_pageHeader = value;
PropertyChanged(this, new PropertyChangedEventArgs("PageHeader"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
MainPage.xaml:
<Grid x:Name="LayoutRoot" Background="White">
<my:PageHeaderControl Header="{Binding PageHeader, Mode=TwoWay}"></my:PageHeaderControl>
</Grid>
MainPage.xaml.cs:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
I'm a little bit confused and I think you missed the syntax of Binding inline expression.
after "{Binding" comes Path to your property. Is "PageHeader" is a path to your property?!
I think you mean this:
<my:PageHeader Header="{Binding PageHeader, Mode=TwoWay}" />
<TextBlock Text="{Binding PageHeader, Mode=TwoWay}" />
The problem is that Binding expression only works when you set the value of property using SetValue method and notify the parent DependencyObject that specific property has changed!
You should use a DependencyProperty to have TwoWay Binding on it, OR implement System.ComponentModel.INotifyPropertyChange interface in your class and notify the Binding object manually by calling PropertyChanged event in the interface.
The definition of PageHeader property should be like this:
public static readonly DependencyProperty PageHeaderProperty = DependencyProperty.Register("PageHeader", typeof(string), typeof(YOUROWNER), new PropertyMetadata(""));
public string PageHeader
{
get { return GetValue(PageHeaderProperty) as string; }
set { SetValue(PageHeaderProperty, value); }
}
Cheers
Related
I am working on a reusable content dialog UserControl that the user can import into their xaml and only have to be responsible for bindings in their respective ViewModel. My UserControl has DependencyProperties tied to appropriate content, but the Content Dialog event's are not visible (programmatically) to users of the UserControl. Specifically, I am looking for .ShowAsync()., but Intellisense does not see .ShowAsync() as being part of UserControl.
I used the examples here as a pattern, but I don't need to extend the DialogContent class for my purpose (I think?): https://learn.microsoft.com/en-us/uwp/api/Windows.UI.Xaml.Controls.ContentDialog
As an experiment, I tried to make this SO work, but I then realized this was sitting on Template 10 framework and Dependency Injection/IoC (I am doing my app in straight C#): UWP ContentDialog Invocation
Here is my UserControl's code behind:
public partial class UserDefinedDialogView : UserControl
{
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("DialogTitle", typeof(string), typeof(UserDefinedDialogView), null);
public static readonly DependencyProperty DialogContentTextProperty =
DependencyProperty.Register("DialogContent", typeof(string), typeof(UserDefinedDialogView), null);
public static readonly DependencyProperty PrimaryButtonTextProperty =
DependencyProperty.Register("DialogPrimaryButtonText", typeof(string), typeof(UserDefinedDialogView), null);
public static readonly DependencyProperty SecondaryButtonTextProperty =
DependencyProperty.Register("DialogSecondaryButtonText", typeof(string), typeof(UserDefinedDialogView), null);
public static readonly DependencyProperty CloseButtonTextProperty =
DependencyProperty.Register("DialogCloseButtonText", typeof(string), typeof(UserDefinedDialogView), null);
// PropertyWrappers
public string DialogTitle
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public string DialogContent
{
get { return (string)GetValue(DialogContentTextProperty); }
set { SetValue(DialogContentTextProperty, value); }
}
public string DialogPrimaryButtonText
{
get { return (string)GetValue(PrimaryButtonTextProperty); }
set { SetValue(PrimaryButtonTextProperty, value); }
}
public string DialogSecondaryButtonText
{
get { return (string)GetValue(SecondaryButtonTextProperty); }
set { SetValue(SecondaryButtonTextProperty, value); }
}
public string DialogCloseButtonText
{
get { return (string)GetValue(CloseButtonTextProperty); }
set { SetValue(CloseButtonTextProperty, value); }
}
public UserDefinedDialogView()
{
this.InitializeComponent();
}
}
The UserControl's xaml:
<UserControl
x:Class="HHPM_NEXT.Views.Common.UserDefinedDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HHPM_NEXT.Views.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<ContentDialog x:Name="UserDefinedDialog"
Title="{x:Bind DialogTitle, Mode=TwoWay}"
Content="{x:Bind DialogContent, Mode=TwoWay}"
PrimaryButtonText="{x:Bind DialogPrimaryButtonText, Mode=TwoWay}"
CloseButtonText="{x:Bind DialogCloseButtonText, Mode=TwoWay}"
x:FieldModifier="Public">
</ContentDialog>
</UserControl>
Example Implementation (References to Zeroize are in a ViewModel, but I didn't add it because I didn't want this to get too long):
<views1:UserDefinedDialogView x:Name="ConfimationDialog" DialogTitle="{Binding ZeroizeTitle, Mode=TwoWay}" DialogContent="{Binding ZeroizeContent, Mode=TwoWay}"
DialogPrimaryButtonText="{Binding ZeroizeConfirmButtonText, Mode=TwoWay}" DialogCloseButtonText="{Binding ZeroizeCloseButtonText, Mode=TwoWay}"/>
I have a usercontrol with couple of controls inside. So I decide to use ViewModel to do managing for all those bindable value. But I find my binding is always null. So how to setup binding for ViewModel in usercontrol
MainWindows.xaml
<Window x:Class="Test.MainWindow"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<cus:Wizard WizardModel="{Binding MyModel}"/>
</StackPanel>
</Window>
MainWindows.xaml.cs
public partial class MainWindow : Window
{
private ViewModel vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = vm;
}
}
ViewModel.cs(MainWindow viewmodel)
public class ViewModel : INotifyPropertyChanged
{
private Model _MyModel;
public Model MyModel
{
get
{
return _MyModel;
}
set
{
_MyModel = value;
NotifyPropertyChanged("MyModel");
}
}
}
Wizard.xaml(my UserControl)
<UserControl mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Something}" />
</Grid>
</UserControl>
Wizard.xaml.cs
public partial class Wizard : UserControl
{
private readonly object modelLock = new object();
private Model CurrentModel = new Model();
public Wizard()
{
InitializeComponent();
DataContext = CurrentModel;
}
public Model WizardModel
{
get { return (Model)this.GetValue(WizardModelProperty); }
set { this.SetValue(WizardModelProperty, value); }
}
public static readonly DependencyProperty WizardModelProperty = DependencyProperty.Register("WizardModel", typeof(Model), typeof(Wizard), new PropertyMetadata(null, new PropertyChangedCallback(ModelChanged)));
private static void ModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Wizard)d).OnModelChanged();
}
private void OnModelChanged()
{
lock (this.modelLock)
{
if(CurrentModel != null)
{
CurrentModel = null;
}
if (WizardModel != null)
{
CurrentModel = WizardModel;
}
}
}
}
The WizardModel in UserControl is always null. So how to setup this ViewModel in UserControl
A UserControl that is supposed to operate on a particular view model class - or more precisely on a class with a particular set of public properties - may directly bind to the view model properties in its XAML.
Given a view model like
public class Model
{
public string Something { get; set; }
}
you may write a UserControl with nothing more than this XAML
<UserControl ...>
...
<TextBox Text="{Binding Something}" />
...
</UserControl>
and this code behind
public partial class Wizard : UserControl
{
public Wizard()
{
InitializeComponent();
}
}
If you now set its DataContext to an instance of Model (or any other class with a Something property), it will just work:
<local:Wizard DataContext="{Binding MyModel}"/>
Since the value of the DataContext property is inherited from parent to child elements, this will also work:
<StackPanel DataContext="{Binding MyModel}">
<local:Wizard/>
</StackPanel>
However, the UserControl still dependends on the existence of a Something property in its DataContext. In order to get rid of this dependence, your control may expose a dependency property
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register(nameof(MyText), typeof(string), typeof(Wizard));
public string MyText
{
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
and bind the element in its XAML to its own property
<UserControl ...>
...
<TextBox Text="{Binding MyText,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
...
</UserControl>
Now you would bind the control's property instead of setting its DataContext:
<local:Wizard MyText="{Binding MyModel.Something, Mode=TwoWay}"/>
I'm working on a Video Player application using C# and WPF.
I have to follow a MVVM model for this WPF project.
I want to use the Meta.Vlc library to display severals RTSP stream in a grid.
So, I add a "ThreadSeparatedImage" object in my VideoPlayControl XAML (view part of the model):
VideoPlayerControl.xaml:
<UserControl x:Class="TVSCS_View.VideoDisplay.VideoPlayerControl"
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:ctrl="clr-namespace:TVSCS_View.VideoDisplay"
xmlns:vlc="clr-namespace:Vlc.Wpf;assembly=Vlc.Wpf"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:helpers="clr-namespace:TVSCS_View.Helpers"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300"
x:Name="controlVideoDisplay"
DataContext="{Binding ElementName=controlVideoDisplay}">
<Border BorderBrush="Black"
BorderThickness="1">
<Grid x:Name="videoPlayerGrid"
Margin="5,5,5,5">
<TextBlock x:Name="videoNameText"
HorizontalAlignment="Left"
Margin="10,10,0,0"
Text="{Binding Path=VideoStreamName, Mode=OneWay}"
VerticalAlignment="Top" Width="100"/>
<vlc:ThreadSeparatedImage x:Name="videoSource"
ThreadImageSource={Binding Path=ImgSource}" />
</Grid>
</Border>
</UserControl>
Then, I have to implement a DependencyProperty to follow a MVVM model. So I modified Meta.Vlc "ThreadSeparatedImage.cs" class, adding the following code:
public static readonly DependencyProperty ThreadImageSourceProperty =
DependencyProperty.RegisterAttached("ThreadImageSource",
typeof(ImageSource),
typeof(ThreadSeparatedImage),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(ImageSourcePropertyChanged)));
private static void ImageSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
ThreadSeparatedImage threadSeparatedImage = obj as ThreadSeparatedImage;
if (null != threadSeparatedImage)
{
threadSeparatedImage.Source = (ImageSource)args.NewValue;
}
}
public static void SetImageSource(UIElement element, ImageSource imageSource)
{
element.SetValue(ThreadImageSourceProperty, imageSource);
}
public static ImageSource GetImageSource(UIElement element)
{
return (ImageSource)element.GetValue(ThreadImageSourceProperty);
}
Finally, I have a view model associated to my "VideoPlayerControl" XAML, with the following properties and methods:
VideoPlayerViewModel.cs
private ImageSource _imageSource;
public ImageSource ImgSource
{
get { return _imageSource; }
set
{
if (true == SetProperty(ref _imageSource, value))
{
RaisePropertyChanged("ImgSource");
}
}
}
public void AddVLCPlayer(VlcPlayer mediaPlayer)
{
mediaPlayer.Stop();
mediaPlayer.LoadMedia(#"rtsp://10.2.92.110:554/profile5/media.smp");
mediaPlayer.Play();
ImgSource = mediaPlayer.VideoSource;
mediaPlayer.VideoSourceChanged += MediaPlayer_VideoSourceChanged;
}
private void MediaPlayer_VideoSourceChanged(object sender, VideoSourceChangedEventArgs e)
{
ImgSource = e.NewVideoSource;
}
"AddVLCPlayer" method could be called at initialization.
With this code, the video stream is neither displayed or updated.
Any idea? Thanks.
A Binding like
ThreadImageSource="{Binding Path=ImgSource}"
in the XAML of your UserControl requires that the DataContext of the control is an instance of your view model, i.e. class VideoPlayerViewModel.
This is usually achieved somewhere in your MainWindow code, where you might have something like this:
public MainWindow()
{
InitializeComponent();
DataContext = new VideoPlayerViewModel();
}
Then the DataContext is inherited by your UserControl somewhere in the MainWindow's XAML tree.
However, this will only work if the UserControl does not explicitly set its DataContext, as you do here:
<UserControl ... x:Name="controlVideoDisplay"
DataContext="{Binding ElementName=controlVideoDisplay}">
Remove that DataContext assignment. As a general rule, never set the DataContext of a UserControl explictly, regardless of what they tell you in online tutorials and blog posts.
Besides that, you should probably avoid modifying the code of the Vlc ThreadSeparatedImage class, and instead create a derived class with your dependency property:
public class MyThreadSeparatedImage : ThreadSeparatedImage
{
public static readonly DependencyProperty ThreadImageSourceProperty =
DependencyProperty.Register(
"ThreadImageSource",
typeof(ImageSource),
typeof(MyThreadSeparatedImage),
new FrameworkPropertyMetadata(null, ThreadImageSourcePropertyChanged));
private static void ThreadImageSourcePropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
((ThreadSeparatedImage)obj).Source = (ImageSource)args.NewValue;
}
public ImageSource ThreadImageSource
{
get { return (ImageSource)element.GetValue(ThreadImageSourceProperty); }
set { element.SetValue(ThreadImageSourceProperty, imageSource); }
}
}
I use a classic Image control too.
In XAML file (view part):
<Image Source="{Binding Path=VideoImageSource}" />
In my associated view model (VidePlayerViewModel.cs):
private ImageSource _videoImageSource;
public ImageSource VideoImageSource
{
get { return _videoImageSource; }
set
{
if (true == SetProperty(ref _videoImageSource, value))
{
RaisePropertyChanged("VideoImageSource");
}
}
}
public void AddVLCPlayer(string videoStreamPath)
{
//Create MediaPlayer:
VlcMediaPlayer = new VlcPlayer(false);
VlcMediaPlayer.Initialize(Globals.pathLibVlc);
//Start player
VlcMediaPlayer.Stop();
VlcMediaPlayer.LoadMedia(videoStreamPath);
VlcMediaPlayer.Play();
//Link VLC player to image source:
VideoImageSource = VlcMediaPlayer.VideoSource;
VlcMediaPlayer.VideoSourceChanged += MediaPlayer_VideoSourceChanged;
}
I have extended the functionality of a textbox, below is part of the class with an attached property RegularExpressionEnabled:
public class AlphaNumericTextBox : TextBox
{
#region DependencyProperties
public static readonly DependencyProperty RegularExpressionEnabledProperty =
DependencyProperty.Register("RegularExpressionEnabled", typeof(bool), typeof(AlphaNumericTextBox),
new UIPropertyMetadata(false));
public bool RegularExpressionEnabled
{
get { return (bool)GetValue(RegularExpressionEnabledProperty); }
set { SetValue(RegularExpressionEnabledProperty, value); Console.WriteLine("RegularExpressionEnabled:" + (bool)value); }
}
}
This class is then incorporated into a UserControl.
XAML:
<UserControl x:Class="Libs_ViewLevel.Controls.FilterItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Libs_ViewLevel"
xmlns:ctrls="clr-namespace:Libs_ViewLevel.Controls"
x:Name="UserControl">
<Grid x:Name="LayoutRoot" Height="Auto" >
<ctrls:AlphaNumericTextBox x:Name="AlphaNumericTbx"
Grid.ColumnSpan="2"
Text="{Binding AlphaNumericValue}"
RegularExpressionEnabled="{Binding RegExpressionEnabled}" />
</Grid>
</UserControl>
Code-Behind:
public partial class FilterItemControl : UserControl
{
public FilterItemControl()
{
InitializeComponent();
this.DataContext = this;
}
public bool RegExpressionEnabled
{
get { return (bool)GetValue(RegExpressionEnabledProperty); }
set { SetValue(RegExpressionEnabledProperty, value); Console.WriteLine("RegExpressionEnabled:" + (bool)value); }
}
public static readonly DependencyProperty RegExpressionEnabledProperty =
DependencyProperty.Register("RegExpressionEnabled", typeof(bool), typeof(FilterItemControl),
new UIPropertyMetadata(false));
I have placed the UserControl in a Window and in the code-behind of this window, placed this statement: txtbox.RegExpressionEnabled = true;
In both RegularExpressionEnabled and RegExpressionEnabled you can see that I have placed a Console.Write().
The RegExpressionEnabled prints to the Output screen, however it does not get to the second, RegularExpressionEnabled.
Something is wrong in my binding on AlphaNumericTextBox RegularExpressionEnabled="{Binding RegExpressionEnabled}", can someone point me in the right direction?
In the code behind of FilterItemControl, RegExpressionEnabled property should be a standard property as this is what your new textbox is binding to. You don't need the dependency property in the FilterItemControl code behind either as you already have it in the new textbox.
Now instead of setting txtbox.RegExpressionEnabled = true (which actually should be txtbox.RegularExpressionEnabled = true), you set the RegExpressionEnabled property to true and the textbox will bind to this.
You will also need to raise the PropertyChanged event in the setter of the RegExpressionEnabled property to trigger the databinding.
I have a user control which contains a textbox and have created a get/set in the usercontrol to get/set the text property of the textbox.
public class OpenFileControl : UserControl
{
StackPanel sp;
public TextBox tb;
public string Text { get { return tb.Text; } set { tb.Text = value; } }
I then want to set this value based on a binding later on -
<gX3UserControls:OpenFileControl Text="{Binding Value}" />
But I get the following exception
A 'Binding' cannot be set on the 'Text' property of type 'OpenFileControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
After some investigation It seems Text needs to be a dependency property, but If I do that I cant work out how to pass the value on to the textbox.
How can I fix this.
Consider using something like this.
Control XAML:
<UserControl x:Class="WpfTestBench.OpenFileControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=Filename, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</UserControl>
Control codebehind:
using System.Windows;
namespace WpfTestBench
{
public partial class OpenFileControl
{
public static readonly DependencyProperty FilenameProperty =
DependencyProperty.Register("Filename", typeof (string), typeof (OpenFileControl));
public OpenFileControl()
{
InitializeComponent();
}
public string Filename
{
get { return (string)GetValue(FilenameProperty); }
set { SetValue(FilenameProperty, value); }
}
}
}
Main XAML:
<Window x:Class="WpfTestBench.OpenFileWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfTestBench="clr-namespace:WpfTestBench"
Title="OpenFileWindow" Width="300" SizeToContent="Height">
<StackPanel>
<wpfTestBench:OpenFileControl x:Name="In" Filename="{Binding SelectedFilename, UpdateSourceTrigger=PropertyChanged}" />
<wpfTestBench:OpenFileControl x:Name="Out" Filename="{Binding ElementName=In, Path=Filename}" />
</StackPanel>
</Window>
Main codebehind:
namespace WpfTestBench
{
public partial class OpenFileWindow
{
public OpenFileWindow()
{
InitializeComponent();
DataContext = this;
}
public string SelectedFilename { get; set; }
}
}
Execution result (after typing something in the first control):
If you define the dependency property as the static and the actual property, you can write whatever code behind you want in the body of the property.
public const string TextPropertyName = "Text";
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
TextPropertyName,
typeof(string),
typeof(MyControl),
new UIPropertyMetadata(false));
In the getter and setter you can do something like textBox1.Text = value; but you'd probably be better served using a binding to the property instead. MVVM frameworks make light work of this sort of thing quite often. You might find more success defining a ViewModel (a class with an appropriate FielPath variable for example) and setting the DataContext of the new UserControl to be an instance of the ViewModel class, using Bindings to do the heavy lifting for you.