How to use a dependency property defined in custom class? - c#

I have a class that define some custom dependency properties for TextBox class:
public class DependencyProperties:FrameworkElement
{
public static readonly DependencyProperty SelectionBeginProperty = DependencyProperty.Register("SelectionBegin", typeof(int), typeof(TextBox),
new UIPropertyMetadata(0, SelectionStartDependencyPropertyChanged));
public static readonly DependencyProperty SelectionLengthProperty = DependencyProperty.Register("SelectionLength", typeof(int), typeof(TextBox),
new UIPropertyMetadata(0, SelectionLengthDependencyPropertyChanged));
public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register("Children", typeof (string), typeof (TreeView));
static DependencyProperties()
{
}
...
}
And when I try to use these properties in Xaml:
<TextBox Name="TextBox_1735"
SelectionBegin="{Binding TextBox_1735SelectionBegin, UpdateSourceTrigger=PropertyChanged}"
SelectionLength="{Binding TextBox_1735SelectionLength, UpdateSourceTrigger=PropertyChanged}" />
It raise an exception that the property SelectionBegin can't be resolved.

What you should be looking for is Attached Properties as the standard dependency properties have to be declared in the control itself. You also could inherit from TextBox and add your dependency properties in the derived class.

I created a simple class that should look something like with some remarks. By this example you can make yourself the remaining properties.
AttachedProperty
public class DependencyProperties
{
#region Here put your property declaration
public static readonly DependencyProperty SelectionBeginProperty;
public static void SetSelectionBegin(DependencyObject DepObject, int value)
{
DepObject.SetValue(SelectionBeginProperty, value);
}
public static int GetSelectionBegin(DependencyObject DepObject)
{
return (int)DepObject.GetValue(SelectionBeginProperty);
}
#endregion
#region Here in constructor register you property
static DependencyProperties()
{
SelectionBeginProperty = DependencyProperty.RegisterAttached("SelectionBegin", // RegisterAttached
typeof(int), // Type of your property
typeof(DependencyProperties), // Name of your class
new UIPropertyMetadata(0, SelectionStartDependencyPropertyChanged));
}
#endregion
private static void SelectionStartDependencyPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Some logic
var textBox = sender as TextBox;
if (textBox == null)
{
return;
}
if (e.NewValue is int && ((int)e.NewValue) > 0)
{
textBox.Background = Brushes.Red;
}
}
}
XAML
<Window x:Class="AttachedPropertyHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AttachedPropertyHelp"
Name="MyWindow" Title="MainWindow"
Height="350" Width="525">
<Grid>
<TextBox Name="MyTextBox"
local:DependencyProperties.SelectionBegin="{Binding Path=Width, ElementName=MyWindow}"
Width="100"
Height="30" />
</Grid>
</Window>
For attached dependency property sets the Width of the Window Int type and if it is greater than zero, then the TextBox marked red Background.

Related

DependencyProperty for TextBox gives compile time error (UWP)

I have a textbox with a DependencyProperty, Code looks like this
<UserControl
x:Class="Projectname.Controls.Editors.EditTextControl"
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:ui="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
mc:Ignorable="d">
<Grid>
<TextBox PlaceholderText="I'am Active" HasError="{Binding IsInvalid, UpdateSourceTrigger=PropertyChanged}" Height="80" Width="300" x:Name="txtActive" Text="{Binding TextValue, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" ></TextBox>
</Grid>
public sealed partial class EditTextControl : UserControl
{
TestViewModel TV = new TestViewModel();
public EditTextControl()
{
this.InitializeComponent();
this.DataContext = TV;
}
public bool HasError
{
get { return (bool)GetValue(HasErrorProperty); }
set { SetValue(HasErrorProperty, value); }
}
/// <summary>
/// This is a dependency property that will indicate if there's an error.
/// This DP can be bound to a property of the VM.
/// </summary>
public static readonly DependencyProperty HasErrorProperty =
DependencyProperty.Register("HasError", typeof(bool), typeof(EditTextControl), new PropertyMetadata(false, HasErrorUpdated));
// This method will update the Validation visual state which will be defined later in the Style
private static void HasErrorUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
EditTextControl textBox = d as EditTextControl;
if (textBox != null)
{
if (textBox.HasError)
VisualStateManager.GoToState(textBox, "InvalidState", false);
else
VisualStateManager.GoToState(textBox, "ValidState", false);
}
}
}
All looks good to me, But on compile-time itself, it's giving these errors.
The property 'HasError' was not found in type 'TextBox'.
The member "HasError" is not recognized or is not accessible.
Can anyone please point out what I am doing wrong here?
HasError is a property on the EditTextControl user control, not on the TextBox.
If you want to add a custom property to the TextBox class, you use an Attached Property not a Dependency property.

How to dynamically bind static DependencyProperty defined in a class to a property in another xaml class?

I have a static DependencProperty IsControlVisibleProperty in MyControl.xaml.cs. And its value is changed inside that same class. And I want to listen to this property in another control Visibility property whenever IsControlVisibleProperty value is changed.
MyControl.xaml.cs :
public partial class MyControl : UserControl
{
public static DependencyProperty IsControlVisibleProperty = DependencyProperty.Register(nameof(IsControlVisible), typeof(bool), typeof(MyControl));
public bool IsControlVisible
{
get{ return (bool)GetValue(IsControlVisibleProperty); }
set { SetValue(IsControlVisibleProperty, value); }
}
// In a function I am updating the dependency property
private void UpdateProp(bool isVisible)
{
this.SetValue(UserControl1.IsControlVisible, isVisible);
}
Now I want to use IsControlVisibleProperty value in another xaml file
SampleControl.xaml:
<UserControl x:Class="SampleControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="http://schemas.microsoft.com/netfx/2007/xaml/presentation"
Height="300" Width="300">
<UserControl.Resources>
<converter:BooleanToVisibilityConverter x:Key="boolToVisConverter"/>
</UserControl.Resources>
<Grid>
<local:MyControl/>
<TextBlock Name="ErrorMessage" Text="Failed to run" Visibility="{Binding ElementName="ErrorMessage" , Path=(local:MyControl.IsControlVisible), Converter={StaticResource boolToVisConverter}, Mode=TwoWay}"/>
</Grid>
So my TextBlock (ErrorMessage) is not binding the IsVisibleProperty from MyControl.xaml.cs. I want IsVisibleProperty to be always binded with TextBlock(whereever the property changes it should alse change its visibility ) not just one time on contruction . Unfortunately I am unable to achieve this . Is there any other way to do so ?
The declaration of a DependencyProperty is always static but your IsControlVisible property is not static. In fact, you can't declare a static dependency property because the GetValue and SetValue method are not static.
What you should is to define an attached dependency property in a static class:
public static class MyProperties
{
public static readonly DependencyProperty IsControlVisibleProperty = DependencyProperty.RegisterAttached(
"IsControlVisible",
typeof(bool),
typeof(MyProperties));
public static void SetIsControlVisible(UIElement element, Boolean value)
{
element.SetValue(IsControlVisibleProperty, value);
}
public static bool GetIsControlVisible(UIElement element)
{
return (bool)element.GetValue(IsControlVisibleProperty);
}
}
You can then set this property on any UIElement (or whatever the type is in your get and set accessors) using the accessors like this:
MyProperties.SetIsControlVisible(this, true); //this = the UserControl
You bind to the attached property of the parent UserControl like this:
<TextBlock Name="ErrorMessage" Text="Failed to run"
Visibility="{Binding Path=(local:MyProperties.IsControlVisible),
Converter={StaticResource boolToVisConverter},
RelativeSource={RelativeSource AncestorType=UserControl}}" />

How to Create a UWP Reusable Content Dialog That is MVVM Compliant

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}"/>

Add a DependencyProperty to ThreadSeparatedImage class of Meta.Vlc library to follow MVVM model

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;
}

Wpf Textbox UpdateSourceTrigger doesn't update the source

I have a simple Window with a TextBox
XAML
<Window x:Class="Configurator.ConfiguratorWindow"
x:Name="ConfigWindow" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox x:Name="DescriptionTextBox" Text="{Binding Path=Description, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
</Window>
in the code behind
public partial class ConfiguratorWindow : Window
{
public ConfiguratorWindow()
{
InitializeComponent();
}
private static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(ConfiguratorWindow), new PropertyMetadata());
public string Description
{
get { return GetValue(DescriptionProperty).ToString(); }
set {
SetValue(DescriptionProperty, value);
_actual_monitor.Description = value;
}
}
}
the graphic is updating right, but when i change the text in the textbox and lose focus it doesn't update the source property.
What is wrong?
DependencyProperties are used for UserControls rather than ViewModel type bindings.
You should
Create a ConfigurationWindowViewModel (Read about MVVM) and implement INotifyPropertyChanged
Create a Property Description that utilizes the INotifyPropertyChanged
Create a new instance of that view model to be set to the DataContext of your ConfigurationWindow.
The getter and setter of the CLR wrapper of a dependency property must not contain any other code than GetValue and SetValue. The reason is explained in the XAML Loading and Dependency Properties article on MSDN.
So remove the _actual_monitor.Description = value; assignment from the setter and add a PropertyChangedCallback to react on property value changes:
public partial class ConfiguratorWindow : Window
{
public ConfiguratorWindow()
{
InitializeComponent();
}
private static DependencyProperty DescriptionProperty = DependencyProperty.Register(
"Description", typeof(string), typeof(ConfiguratorWindow),
new PropertyMetadata(DescriptionPropertyChanged));
public string Description
{
get { return (string)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
private static void DescriptionPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ConfiguratorWindow obj = d as ConfiguratorWindow;
obj._actual_monitor.Text = (string)e.newValue;
}
}
Try this
<Window x:Class="Configurator.ConfiguratorWindow"
xmlns:myWindow="clr-namespace:YourNamespace"
x:Name="ConfigWindow" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox x:Name="DescriptionTextBox" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type myWindow}}, Path=Description, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
public partial class ConfiguratorWindow : Window
{
public ConfiguratorWindow()
{
InitializeComponent();
}
private static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(ConfiguratorWindow), new PropertyMetadata(null, CallBack);
private static void callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var foo = d as ConfiguratorWindow ;
all you need to do, you can do here
}
public string Description
{
get { return GetValue(DescriptionProperty).ToString(); }
set { SetValue(DescriptionProperty, value);}
}
}
But it would be much easier to just have a View Model and bind to property there.

Categories