How to create/ WPF Dependency Property in UserControl? - c#

How to create/ WPF Dependency Property in UserControl?
Is it possible to create it through viewmodel?
public partial class SomeView : UserControl
{
SomeViewModel vm = new SomeViewModel(ForeColor);
public SomeView()
{
InitializeComponent();
this.DataContext = vm;
}
public Color ForeColor
{
get { return (Color)this.GetValue(ForeColorProperty); }
set { this.SetValue(ForeColorProperty, value); }
}
public static readonly DependencyProperty ForeColorProperty = DependencyProperty.Register("ForeColor", typeof(Color), typeof(SomeView));
}
and then calling the control like below doesn't work.
<local:SomeView ForeColorProperty="{Binding Foreground}"/>

You reference it as "ForeColor", not "ForeColorProperty".
<local:SomeView ForeColor="{Binding Foreground}"/>
For the above binding to work, there has to be a public property "Foreground" of type "Color" in the control's current data context.
Edit
If you want to pass the value to the view model, then you need a two-way binding:
<local:SomeView ForeColor="{Binding Foreground,Mode=TwoWay}"/>

Related

Binding to property of UserControl doesn't work

I got a custom TextBox which I plan to include in another UserControl, however when setting up the Binding for it, it simply just doesn't bind.
I simplified the code for clarity.
My custom TextBox:
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
partial class CustomTextBox : UserControl
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(String.Empty));
}
This binding works as expected. When using CustomTextBox in another UserControl or Window, I can access the property just as expected.
The following code blocks describe the UserControl that uses CustomTextBox and the corresponding ViewModel with the property I want to bind Text to.
<UserControl>
<UserControl.DataContext>
<vm:MyViewModel />
</UserControl.DataContext>
<local:CustomTextBox Text="{Binding FooBar, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
public class MyViewModel : INotifyPropertyChanged
{
private string _fooBar;
public string FooBar
{
get { return _fooBar = (_fooBar ?? ""); }
set
{
_fooBar = value; OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
My problem occurs exactly when I want to bind the Text property to a ViewModel in another UserControl, it just doesn't work. In this case I tried to bind the Text property to the FooBar property on the MyViewModel class, however changes to the Text property do not get reflected on the FooBar property and vice-versa. However when I hover over the binding in the XAML view, it shows the type of the property, so I don't exactly see what's wrong here.
My best guess is that it has to do with two bindings accessing the same property.
modify DP registration to include FrameworkPropertyMetadataOptions.BindsTwoWayByDefault option
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomTextBox),
new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Is it a bad practice to define a ViewModel as a DependencyProperty for data-binding?

I have a UserControl MyView, which has an inner UserControl SubView.
The view models of the 2 UserControls have the same hierarchical structure as the views,
i,e, MyViewModel has SubViewModel inside as shown in the code below.
public class MyViewModel
{
private readonly SubViewModel _subViewModel = new SubViewModel();
public SubViewModel SubViewModel { get { return _subViewModel; } }
private void HandleSubViewModel()
{
// Do what is necessary to handle SubViewModel
}
}
My question is about how to bind SubViewModel to SubView.
Now I define SubViewModel in the code-behind of SubView and bind it to the SubViewModel property of MyViewModel class.
public partial class SubView : UserControl
{
public static readonly DependencyProperty SubViewModelProperty = DependencyProperty.Register(
"SubViewModel", typeof (SubViewModel), typeof (SubView), new PropertyMetadata(default(SubViewModel)));
public SubViewModel SubViewModel
{
get { return (SubViewModel) GetValue(SubViewModelProperty); }
set { SetValue(SubViewModelProperty, value); }
}
}
<UserControl x:Class="MyProject.View.MyView"
xmlns:parts="clr-namespace:MyProject.View.Parts">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=MyViewModel}">
<parts:SubView SubViewModel="{Binding SubViewModel}"/>
</Grid>
</UserControl>
Is it a bad practice to bind an inner view model in this way?
If so, how can I bind it in a better way?
One reason why it could be bad is that if a property inside the SubViewModel changes, there is no way that the MyViewModel would know about that. So for e.g. if there are any validations that you need to carry out and handle at the MyViewModel level, you would not be able to do that.
To work around that, when a property changes; you would have to raise an event in the SubViewModel and make the MyViewModel subscribe to it and react appropriately after receiving it.
Apart from that I see no drawbacks as such. But do read these links for further info:
MVVM and nested view models
MVVM: How to handle interaction between nested ViewModels?
You can do like this:
View models:
public class MyViewModel
{
private readonly SubViewModel _subViewModel;
public SubViewModel SubViewModel
{
get { return _subViewModel; }
}
public MyViewModel()
{
_subViewModel = new SubViewModel();
_subViewModel.Text1 = "blabla";
}
}
public class SubViewModel : DependencyObject
{
public string Text1
{
get { return (string)GetValue(Text1Property); }
set { SetValue(Text1Property, value); }
}
public static readonly DependencyProperty Text1Property =
DependencyProperty.Register("Text1", typeof(string), typeof(SubViewModel));
}
SubUserControl:
<UserControl x:Class="WpfApplication1.SubUserControl"
xmlns= ... >
<Grid Height="200" Width="300" Background="Aqua">
<TextBlock Text="{Binding SubViewModel.Text1}" />
</Grid>

How can I bind a WPF checkbox's IsChecked property to a boolean property of an object that is not a window

I found a lot of examples on how to bind the IsChecked property of a WPF checkbox to a boolean property, if both belong to the same Window class. I want to do a different thing:
I have the main window (excerpt):
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private readonly SoundFx _soundFx = new SoundFx();
private void _StartNewGame()
{
_soundFx.GameStarted();
}
}
Then I have the SoundFx class (excerpt):
public class SoundFx : DependencyObject
{
public void GameStarted()
{
if (Enabled)
{
_PlayGameStartedSound();
}
}
public bool Enabled
{
get { return (bool) GetValue(EnabledProperty); }
set { SetValue(EnabledProperty, value); }
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.Register("Enabled", typeof(bool),
typeof(SoundFx), new UIPropertyMetadata(false));
}
And I have the XAML (excerpt):
<Grid>
<CheckBox IsChecked="{Binding ElementName=_soundFx, Path=Enabled}" x:Name="checkBoxSoundFx" Content="Sound FX" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom"/>
</Grid>
To be honest, I'm new to WPF and don't know exactly what I'm doing. What I'm trying to achieve is that the value of _soundFx.Enabled be changed when the user clicks on the checkBoxSoundFx element, without using any event handlers like Checked or Unchecked. This should be possible with data binding, shouldn't it?
First you need to create
public SoundFx _soundFx { get; set; }
as public property, because you cannot bind to private field
public MainWindow()
{
InitializeComponent();
_soundFx = new SoundFx();
}
And from xaml you need to bind like:
<CheckBox IsChecked=
"{Binding RelativeSource=
{RelativeSource Mode=FindAncestor,AncestorType=Window},
Path=_soundFx.Enabled}"}"
x:Name="checkBoxSoundFx"
Content="Sound FX"
HorizontalAlignment="Right"
Margin="0,0,10,10"
VerticalAlignment="Bottom"/>
You were close, you need a property to bind to and you need to set the DataContext if you didn't do it:
public partial class MainWindow
{
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
private readonly SoundFx _soundFx = new SoundFx();
public SoundFx {get {return _soundFx;}}
private void _StartNewGame()
{
_soundFx.GameStarted();
}
}
You then need to bind to this property (and set the mode to OneWayToSource if you only need to set the property, never update the CheckBox according to the property value):
<Grid>
<CheckBox IsChecked="{Binding Path=SoundFx.Enabled, Mode=OneWayToSource}" x:Name="checkBoxSoundFx" Content="Sound FX" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom"/>
</Grid>
By the way I'm not sure why you SoundFx is a DependencyObject and why your Enabled property is a DependencyProperty. A simple property would work aswell in this particular example.
DependencyProperties are useful when you want to set them in a Style or animate them with a Storyboard for example, you don't seem to be in this case. I think SoundFx should inherit DependencyObject and Enabled should be a simple property (This is an opinion I make knowing very little about your project though).
As I've managed to grow more experienced in WPF in the meantime, I would now say that my question itself was wrong. In order to avoid confusion in binding and unnecessary dependencies between view and model, I would now always prefer MVVM for cases like this.
Example: https://codereview.stackexchange.com/questions/124361/mvvm-am-i-doing-it-right

WPF bind to content control's content property

How can I bind to content control's content property ?
I'v created custom control :
public class CustomControl
{
// Dependency Properties
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(MainViewModel), new PropertyMetadata(0));
}
In ViewModel I created a property of type of this custom control :
public CustomControl CustomControl { get; set; }
In view I bind this property to content control :
<ContentControl x:Name="Custom" Content="{Binding CustomControl}"></ContentControl>
Now how can I bind to content control's content property?
<ContentControl Content="{Binding ElementName=Custom, Path=Content}" />
I'm not sure what effect this will have though. I have a suspicion it will complain about UI elements already having a parent or something similar.
Update
If I think I understand your question correctly I don't think you can do what you want using bindings. This is an alternative which adds a callback for when the content is changed so you can set the new content to the property of your VM:
class CustomControl : Control
{
static CustomControl()
{
ContentControl.ContentProperty.OverrideMetadata(typeof(CustomControl), new PropertyMetadata(null, UpdateViewModel));
}
private static void UpdateViewModel(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as CustomControl;
var viewModel = control.DataContext as MyViewModel;
viewModel.CustomControl = control;
}
}
You'll probably want some error handling in there.

Access bound object in UserControl code behind with DependencyProperty

I am having trouble setting a property of a custom user control using a DependencyProperty through databinding on the parent UserControl.
Here is the code for my custom UserControl:
public partial class UserEntityControl : UserControl
{
public static readonly DependencyProperty EntityProperty = DependencyProperty.Register("Entity",
typeof(Entity), typeof(UserEntityControl));
public Entity Entity
{
get
{
return (Entity)GetValue(EntityProperty);
}
set
{
SetValue(EntityProperty, value);
}
}
public UserEntityControl()
{
InitializeComponent();
PopulateWithEntities(this.Entity);
}
}
I want access to the Entity property in the code behind because that will dynamically build the user control based on values stored in the Entity. The problem that I am having is that the Entity property is never set.
Here is how I am setting up the binding in the parent user control:
<ListBox Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding SearchResults}" x:Name="SearchResults_List">
<ListBox.ItemTemplate>
<DataTemplate>
<!--<views:SearchResult></views:SearchResult>-->
<eb:UserEntityControl Entity="{Binding}" ></eb:UserEntityControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I am setting the ItemsSource of the ListBox to SearchResults, which is an Observable Collection of Entities (The same type as Entity on the custom UserControl).
I am not getting any runtime binding errors in the debug output window. I just cannot set the value of the Entity property. Any ideas?
You are trying to use the Entity property in the c-tor, which is too soon. the c-tor is going to be fired BEFORE the property value is going to be given.
What u need to do is to add a propertyChanged Event HAndler to the DependencyProperty, like so:
public static readonly DependencyProperty EntityProperty = DependencyProperty.Register("Entity",
typeof(Entity), typeof(UserEntityControl), new PropertyMetadata(null, EntityPropertyChanged));
static void EntityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myCustomControl = d as UserEntityControl;
var entity = myCustomControl.Entity; // etc...
}
public Entity Entity
{
get
{
return (Entity)GetValue(EntityProperty);
}
set
{
SetValue(EntityProperty, value);
}
}

Categories

Resources