In theory this code should provide me with a 300x300 window with a blue background from having the window's content bound to an object of type AstRootViewModel, however this doesn't seem to be the case. I'm wondering it this is happening because I don't call astApplication.Run() until after I set the mainWindow.ViewModel property. Using snoop to check the binding I have a blank content binding and it's flagged as an error with no error information.
If it is the case that property notification does not occurr until the application Run method is called, then what would be the best way to resolve this in an MVVM friendly way?
I have the following Entry point to a WPF application:
[STAThread]
public static void Main()
{
settingsSource = LoadSettingsFile(".\\applicationSettings.xml");
astApplication = new Application();
mainWindow = new AstWindowView();
mainWindowModel = new AstRootViewModel();
dataModel = new AstDataModel(settingsSource);
mainWindow.ViewModel = mainWindowModel;
astApplication.MainWindow = mainWindow;
astApplication.Run();
}
The AstWindowView class implements the following significant code behind:
public partial class AstWindowView : Window
{
public AstRootViewModel ViewModel
{
get { return (AstRootViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
// Using a DependencyProperty as the backing store for ViewModel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(AstRootViewModel), typeof(Window), new UIPropertyMetadata(null));
public AstWindowView()
{
InitializeComponent();
}
}
and the following significant XAML
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="AstViewResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.Content>
<Binding Path="ViewModel" Mode="Default" UpdateSourceTrigger="PropertyChanged"/>
</Window.Content>
The AstViewResources.xaml file defines the following DataTemplate
<DataTemplate DataType="{x:Type vm:AstRootViewModel}">
<vw:AstRootView/>
</DataTemplate>
And lastly, the AstRootView XAML contains the following significant XAML:
<UserControl x:Class="SEL.MfgTestDev.AutomatedSettingsTransfer.View.AstRootView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="#FF000CFF"/>
</UserControl>
It looks like you're not setting the DataContext for AstWindowView anywhere and your Binding has no explicit Source set. Are you seeing a binding error saying something to that effect in your debug output? Try adding after the InitializeComponent call in AstWindowView ctor (could also do it by changing the Binding in XAML):
DataContext = this;
Related
I'm trying to understand dependency property and learn how to use it. I'm going through articles and in this article https://www.c-sharpcorner.com/UploadFile/6d590d/wpf-dependency-property/ there's this example:
MainWindow.xaml:
<Window x:Class="WpfApplication1.DependencyPropertyDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="DependencyPropertyDemo" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<local:CarDependencyClass x:Key="carDependencyClass"></local:CarDependencyClass>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Enter Car:" Grid.Row="0" VerticalAlignment="Center" />
<TextBox Text="{Binding Path=MyCar, Source={StaticResource carDependencyClass }}" Name="MyTextCar" Height="25" Width="150" />
<Button Name="MyButton" Content="Click Me!" Height="25" Click="MyButton_Click" Width="150" Grid.Row="1" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace WpfApplication1 {
/// <summary>
/// Interaction logic for DependencyPropertyDemo.xaml
/// </summary>
public partial class DependencyPropertyDemo : Window {
public DependencyPropertyDemo() {
InitializeComponent();
}
private void MyButton_Click(object sender, RoutedEventArgs e) {
CarDependencyClass dpSample = TryFindResource("carDependencyClass") as CarDependencyClass;
MessageBox.Show(dpSample.MyCar);
}
}
public class CarDependencyClass : DependencyObject {
//Register Dependency Property
public static readonly DependencyProperty CarDependencyProperty = DependencyProperty.Register("MyProperty", typeof(string), typeof(CarDependencyClass));
public string MyCar {
get {
return (string)GetValue(CarDependencyProperty);
}
set {
SetValue(CarDependencyProperty, value);
}
}
}
}
It works. I noticed that they registered dependency property with the name "MyProperty" and that it isn't used anywhere in the program. Only normal CLR property MyCar is used in xaml.
But then there's another article https://www.c-sharpcorner.com/article/simplest-wpf-dependency-property-for-beginners-on-background-color/. And they provide other example:
MainWindow.xaml:
<Window x:Class="DependencyPropertyTutorial.MainWindow" 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:views="clr-namespace:DependencyPropertyTutorial" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DependencyPropertyTutorial" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="BG" Color="Green" />
</Window.Resources>
<Grid>
<views:CustomButtonControl SetBackground="{DynamicResource BG}"></views:CustomButtonControl>
</Grid>
</Window>
CustomButtonControl.xaml:
<UserControl x:Class="DependencyPropertyTutorial.CustomButtonControl"
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:local="clr-namespace:DependencyPropertyTutorial"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button x:Name="btnCustom" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Height="52" Click="btnCustom_Click" />
</Grid>
</UserControl>
CustomButtonControl.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace DependencyPropertyTutorial {
/// <summary>
/// Interaction logic for CustomButtonControl.xaml
/// </summary>
public partial class CustomButtonControl : UserControl {
public CustomButtonControl() {
InitializeComponent();
}
public static readonly DependencyProperty btnDependencyProperty = DependencyProperty.Register("SetBackground", typeof(SolidColorBrush), typeof(CustomButtonControl), new PropertyMetadata(new SolidColorBrush(Colors.HotPink), new PropertyChangedCallback(OnSetColorChanged)));
public SolidColorBrush SetBackground {
set {
SetValue(btnDependencyProperty, value);
}
get {
return (SolidColorBrush)GetValue(btnDependencyProperty);
}
}
private void btnCustom_Click(object sender, RoutedEventArgs e) {
this.SetBackground = new SolidColorBrush(Colors.IndianRed);
}
private static void OnSetColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
CustomButtonControl mycontrol = d as CustomButtonControl;
mycontrol.callmyInstanceMethod(e);
}
private void callmyInstanceMethod(DependencyPropertyChangedEventArgs e) {
btnCustom.Background = (SolidColorBrush)e.NewValue;
}
}
}
And here they register dependency property with the name "SetBackground" which is exactly the same as the name of CLR property - SetBackground. And if I change dependency property(the one I registered with Register method) "SetBackground" to something else, like "SetBackgroundDependencyProperty", then I get a XAML exception when trying to run the application. But "SetBackground" dependency property isn't even referenced in xaml anywhere. Only CLR property SetBackground is referenced in XAML at the line
<views:CustomButtonControl SetBackground="{DynamicResource BG}"></views:CustomButtonControl>
I also get an error in Visual Studio with this example:
But when I try to build and run the application, it works.
So my questions are: why in the first example they didn't have to name registered dependency property the same as CLR property, but in the second example, I have to name registered dependency property the same as CLR property. Is there a way to name registered dependency property differently to the CLR property in the second example? Why and how xaml even uses dependency properties, considering xaml references only CLR properties anyway? I checked it and in both projects only CLR properties are referenced from XAML, according to VS IntelliSense. Why do I have to register dependency property with the same name as CLR property - "SetBackground" when in xaml only CLR property is referenced and it returns SolidColorBrush from the dependency property, anyway:
return (SolidColorBrush)GetValue(btnDependencyProperty);
Here's the solution with both examples:
https://github.com/KulaGGin/DependencyProperty
First example is a bit dirty, I wouldn't code it such way. There is a good convention to avoid a confusion - to name the DP as the CLR property + 'Property'(but it's not mandatory!) and register it as name of CLR property(if you want to use it as DP in XAML).
First to your questions:
First example does work, because of everywhere, where the property MyCar being used, it is used as CLR property. If you will try to bind to the MyCar, it will fail, because of there is no such a dependency property. To implement the functionality in this example would be enough just to declare a CLR property:
public string MyCar { get; set; }
instead of all this confusion with dependency property.
In second example CLR property as well as dependency property SetBackground are defined (the name btnDependencyProperty for the field is not convenient, but OK). Missunderstanding on your side, is what being used in XAML.
If you use in XAML Binding or DynamicResource the dependency property as well as CLR property are necessary! Therefore they need to have the same name. If there aren't, then you will get an error.
If you set the property to the StaticResource or directly to the value or even do not use it in XAML, then you will be able to run the application.
DependencyProperty implementation is slightly weird.
The XAML compiler depends on the CLR property wrapper in order to compile, but at runtime bindings ignore it completely and just call GetValue/SetValue on the DP. Therefore the names should match.
There was a typo in the first example. The registered dependency property name needs to be the same as the CLR-backing property.
Declaring a DependencyProperty is a two-stage process:
Register the property so that the WPF DependencyProperty system can track it and notify when properties change, etc.
Setup a CLR property that gives developers an API to get and set values.
I hope this helps.
Suppose I have a UserControl which has a DataContext set to it's VM
Now suppose the UserControl has a dependency property defined that I want to initially bind to a property on the ViewModel ...how would I reference this property in the XAML of the UserControl given the top-level element is UserControl?
<UserControl x:Class="TestApp1.TestControl"
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:local="clr-namespace:TestApp1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="test">
</UserControl>
Dependency property:
public static readonly DependencyProperty MyTestPropProperty= null;
public string MyTestProp
{
get { return (string)GetValue(MyTestPropProperty); }
set { SetValue(MyTestPropProperty, value); }
}
static TestControl()
{
MyTestPropProperty= DependencyProperty.Register(
"MyTestProp",
typeof(string),
typeof(TestControl),
new FrameworkPropertyMetadata());
}
On the XAML I"d like to be able go MyTestProp={Binding yadda yadda} but I have no way to reference it from XAML
Just add the property in the root node like so:
<Usercontrol xmlns=blablabla
MyProperty="{Binding MyViewModelProperty}">
Edit: This doesn't work because there is no XAML reference to the newly created property. Either use a <Style TargetType="TestControl"> or set the property from an outer context (the one that contains this usercontrol)
I'm building a custom UserControl in WPF, which has a ViewModel associated. I also want do dynamically make controls in the code behind. But now I'm having problems binding the generated controls with the ViewModel properties. My code is:
<UserControl x:Class="SVT.Teste.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="UserControl1ViewModel">
<Grid Name="GridContainer">
</Grid>
</UserControl>
and code behind:
public UserControl1()
{
InitializeComponent();
System.Windows.Controls.Button newBtn = new Button();
newBtn.SetBinding(Button.ContentProperty, new Binding("Test"));
GridContainer.Children.Add(newBtn);
}
public class UserControl1ViewModel
{
private string test = "ola";
public string Test
{
get { return test; }
}
}
When I run this I get:
"System.Windows.Data Error: 40 : BindingExpression path error: 'Test'
property not found on 'object' ''String' (HashCode=-946585093)'.
BindingExpression:Path=Test; DataItem='String' (HashCode=-946585093);
target element is 'Button' (Name=''); target property is 'Content'
(type 'Object')"
Can you help me?
You are setting DataContext property of UserControl1 to a string instead of your view model instance.
You need to do something like this:
<UserControl xmlns:local="clr-namespace:NAMESPACE_WHERE_VIEWMODEL_IS_DEFINED">
<UserControl.DataContext>
<local:UserControl1ViewModel />
</UserControl.DataContext>
<!-- unrelated code omitted -->
</UserControl>
You are setting you DataContext to the type, not an instance that has the properties.
In your method that creates the user control do :
public UserControl1()
{
InitializeComponent();
System.Windows.Controls.Button newBtn = new Button();
newBtn.SetBinding(Button.ContentProperty, new Binding("Test"));
GridContainer.Children.Add(newBtn);
**DataContext = new UserControl1ViewModel();**
}
You still have more work to do. The way you have it no notifications or update will happen. Implement the INotifyPropertyChanged interface (on UserControlViewModel). And remove setting DataContext in the XAML to the type.
try with this binding
Binding MyBinding = new Binding();
MyBinding.Path = new PropertyPath("Test");
newBtn.DataContext = new UserControl1ViewModel(); //or MyBinding.Source = new //UserControl1ViewModel();
newBtn.SetBinding(Button.ContentProperty, MyBinding);
max is right, but i have another question. why do you want create your usercontrol dynamic when you have a viemwodel you wanna bind to? makes no sense to me. let me explain:
if you have a viewmodel - you know in mind how this viewmodel should be rendered and what the bindings are. so you could create a usercontrol/view for this viewmodel
MyUserControl1View.xaml
<UserControl>
<Grid>
<Button Content="{Binding Test}"/>
<!-- more controls and binding if the viewmodel expose more-->
</Grid>
</UserControl>
so what you have now is a representation of your viewmodel. they are not connnected but your viewmodel should look like this and the binding are set. till now no datacontext is set at all.
all you have to do now is to go the viewmodel first approach and the use of datatemplates.
let us assume the following in your mainwindow
<Window>
<Window.Resources>
<DataTemplate Datatype="{x:Type local:Usercontrol1viewmodel}">
<view:MyUserControl1View/>
</DataTemplate>
</Window.Resources>
now wpf knows how to render Usercontrol1viewmodel.
one step more in your mainwindow viewmodel you handle your Usercontrol1viewmodel.
public Usercontrol1viewmodel MyWhatEver {get;set;}
if you bind this property to a contentpresenter, you will see the wpf magic;)
<ContentPresenter Content="{Binding MyWhatEver}"/>
now you see the MyUserControl1View in the contentpresenter, no dynamic view code needed. just go with your viewmodels.
ps: feel free to edit my bad english.
I am trying to populate a custom string array in XAML, but am receiving an error. I subclassed a ComboBox and added a string [] that I want to fill with custom values:
public class MyComboBox : ComboBox
{
public string[] MyProperty { get { return (string[])GetValue(MyPropertyProperty); } set { SetValue(MyPropertyProperty, value); } }
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(string[]), typeof(MyComboBox));
}
My XAML is as follows:
<Window x:Class="Samples.CustomArrayProperty"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Samples"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="CustomArrayProperty" Height="300" Width="300">
<Grid>
<local:MyComboBox Height="20">
<local:MyComboBox.MyProperty>
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</local:MyComboBox.MyProperty>
</local:MyComboBox>
</Grid>
</Window>
When I run this, I get the error: "'Monday' is not a valid value for property 'MyProperty'.".
What am I doing wrong?
You can create arrays in XAML using x:Array, you still need to use it as a resource like Liz's answer.
<Window.Resources>
<x:Array Type="sys:String" x:Key="days">
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</x:Array>
</Window.Resources>
<local:MyComboBox Height="23" MyProperty="{StaticResource days}" />
Is there any other reason you are subclassing the ComboBox beyond what you are doing here?
Because if the idea is to just provide a list of strings to the combobox then take a look at "Add collection or array to wpf resource dictionary"
As much as possible it would be better to let the combobox take care of itself - by using the ItemsSource property. So what we are doing in the linked example is providing a 'resource' containing your list of strings, and then passing this resource to the ItemsSource as follows:
<ComboBox ItemsSource="{StaticResource stringList}" />
Define a type like this:
public class StringList : List<string> { }
And then create a static resource
<Window.Resources>
<local:StringList x:Key="stringList">
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</local:StringList >
</Window.Resources>
I hope that helps.
Edit: You could also change your DependencyProperty to use StringList instead of String[], then your dependency property will work as well.
<local:MyComboBox MyProperty="{StaticResource stringList}" Height="23" />
And then the dependencyProperty:
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty",typeof(StringList),typeof(MyComboBox),new FrameworkPropertyMetadata(null, listChangedCallBack));
static void listChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
ComboBox combo = (ComboBox)property;
combo.ItemsSource= (IEnumerable)args.NewValue;
}
Then this would effectively do the same thing as binding directly to the ItemsSource. But the main thing if I understand you correctly, is just to get the Dependency property to work.
And, an extra example with enum:
namespace MyNamespace
{
public enum OrganizationType
{
Office365,
OnPremisses
}
}
xmlns:local="clr-namespace:MyNamespace"
<x:Array Type="local:OrganizationType" x:Key="OrganizationTypeArray">
<local:OrganizationType>Office365</local:OrganizationType>
<local:OrganizationType>OnPremisses</local:OrganizationType>
</x:Array>
Assuming we have such control:
public partial class MyUserControl : UserControl
{
public MyUserControl() {
InitializeComponent();
}
public string Foo { get; set; }
}
How can I set "Foo" property value declaratively in MyUserControl.xaml?
<UserControl x:Class="Test.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Looking for some thing like this -->
<Foo>Hola</Foo>
</UserControl>
To be more clear: How can I set in XAML a value for the property defined in code-behind.
I've found in the past that you can use a style to set properties on a UserControl from xaml without inheritance. Try something like this:
<UserControl x:Class="Test.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:Test.MyUserControl" >
<UserControl.Style>
<Style>
<Setter Property="c:MyUserControl.Foo" Value="Hola" />
</Style>
</UserControl.Style>
</UserControl>
This can only be achieved in xaml by inheritance:
You are providing an implementation for your control. So the only way to achieve a value in xaml for your control is through inheritance.
Your second UserControl will look like this:
<Test:MyUserControl x:Class="Test.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Test="clr-namespace:Test"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Test:MyUserControl.Foo>Hola</Test:MyUserControl.Foo>
</Test:MyUserControl>
or:
<Test:MyUserControl x:Class="Test.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Test="clr-namespace:Test"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Foo="Hola">
</Test:MyUserControl>
In your control set it in the constructor
public MyUserControl{ this.Foo = "Hola";}
If you're using the control somewhere else:
<Window xmlns:mycontrol="Test" ....
Intellisense will help you here with proper syntax of xmlns
<mycontrol:MyUserControl Foo="Hola"/>
I'm not sure, but Foo may probably need to be a DependencyProperty.
It seems like what you really want is a default value for properties on your UserControl. Unfortunately you can't set it in a Style unless it's a DependencyProperty. It also can't be the target of a Binding.
If Foo were a DependencyProperty, you could set it in the PropertyMetadata when you declare your DependencyProperty:
public static readonly DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(String), typeof(MyUserControl), new PropertyMetadata("Hola"));
Otherwise you're probably better off setting the default value in code-behind.
What about a non string property. e.g. MyCustomObject=