Modify ObjectDataProvider - c#

I have an application where I am using a ObjectDataProvider (App.xaml):
<Application x:Class="Example.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:Example.Settings"
StartupUri="MainWindow.xaml"
Startup="Application_Startup"
DispatcherUnhandledException="ApplicationDispatcherUnhandledException">
<Application.Resources>
<ObjectDataProvider x:Key="odpSettings" ObjectType="{x:Type src:AppSettings}"/>
</Application.Resources>
My class is:
class AppSettings : INotifyPropertyChanged
{
public AppSettings()
{
}
Color itemColor = Colors.Crimson;
public Color ItemColor
{
get
{
return itemColor;
}
set
{
if (itemColor == value)
return;
itemColor = value;
this.OnPropertyChanged("ItemColor");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then I have a userControl where I am using that color, for example:
<Border Background="{Binding Source={StaticResource odpSettings},
Path=ItemColor,Mode=TwoWay}" />
I am adding that UserControl to my MainWindow where I have a ColorPicker control and I would like to modify the Border Background color of my UserControl with the ColorPicker color selected.
I tried something like this:
AppSettings objSettings = new AppSettings();
objSettings.ItemColor = colorPicker.SelectedColor;
When I change the color using the ColorPicker, the color in my UserControl doesn't change, I guess this is because I am creating a new instance of the class AppSettings.
Is there a way to accomplish what I am trying to do?
Thanks in advance.
Alberto

Thanks for the comments, I used the next code:
AppSettings objSettings = (AppSettings)((ObjectDataProvider)Application.Current.FindResource("odpSettings")).ObjectInstance;
That way I can access and modify the value of the property ItemColor.
Also I change the property type to SolidColorBrush.
objSettings.ItemColor = new SolidColorBrush(colorPicker.SelectedColor);

Related

Cannot bind to public property inside ResourceDirectory WinUI3/UWP

So I am currently trying to bind a public static SolidColorBrush to a "Filled" property on a Rectangle inside a ResourceDirectory Style (inisde App.xaml).
But I always get the Exception
"Type 'x' used after '{' must be a Markup Extention. Error code 0x09c6."
My xaml code looks like this (inside App.xaml):
<Rectangle
x:Name="SelectionIndicator"
Width="4"
Height="24"
Fill="{x:Bind SomeNamespace:SomeClass.SomeColor}"/>
and my codebehind looks like this (inside public class called "SomeClass" in Namespace called "SomeNamespace"):
public static SolidColorBrush SomeColor = new(Colors.Red);
I know its possible to bind to a public static property with x:Bind in WinUI/UWP but this somehow doesnt work.
I really need to bind from some code behind so I can implement INotifyPropertyChanged so I cant just create a new color resource.
What is the correct way to implement my wanted behavior?
Thanks in advance!
You probably can't call your PropertyChanged event handler from a static property. You'll need an instance.
Consider storing the brush as a non-static property in a wrapper class. You can instantiate this class as a resource in your App.xaml file and use it across your application.
ColorStorer is a class to store the SolidColorBrush:
public class ColorStorer: INotifyPropertyChanged
{
private SolidColorBrush scb = new SolidColorBrush(Colors.Red);
public SolidColorBrush Scb
{
get
{
return scb;
}
set
{
if (value as SolidColorBrush != null)
{
scb = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Instantiate it in Application.Resources in App.xaml:
<Application.Resources>
<ResourceDictionary>
<local:ColorStorer x:Key="TestAppColorStorer" />
</ResourceDictionary>
</Application.Resources>
Use it in your views and controls:
<Rectangle
Width="100"
Height="50"
HorizontalAlignment="Center"
Fill="{Binding Scb, Source={StaticResource TestAppColorStorer}, Mode=OneWay}" />

ItemsSource Binding in UserControl doens't work [duplicate]

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
XAML binding not working on dependency property?
(1 answer)
Closed 3 years ago.
i am currently testing UserControls and therefore created this little App.
Main.xaml
<Grid>
<control:CustomInterfaceGrid Color="Green" Height="400" CustomItemsSource="{Binding Packages}"></control:CustomInterfaceGrid>
</Grid>
UserControl.xaml
<UserControl x:Class="App.Custom.CustomInterfaceGrid"
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="App.Custom"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
x:Name="SourceElement"
>
<Grid>
<ListBox x:Name="listView" Background="{Binding Color, ElementName=SourceElement}" ItemsSource="{Binding CustomItemsSource, ElementName=SourceElement}"></ListBox>
</Grid>
</UserControl>
CodeBehind from UserControl
public partial class CustomInterfaceGrid : UserControl, INotifyPropertyChanged
{
public CustomInterfaceGrid()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("Color", typeof(SolidColorBrush), typeof(CustomInterfaceGrid));
public SolidColorBrush Color
{
get; set;
}
public static readonly DependencyProperty CustomItemsSourceProperty =
DependencyProperty.Register("CustomItemsSource", typeof(IEnumerable<Object>), typeof(CustomInterfaceGrid));
public IEnumerable<Object> CustomItemsSource
{
get
{
return GetValue(CustomItemsSourceProperty) as IEnumerable<Object>;
}
set {
SetValue(CustomItemsSourceProperty, value);
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The problem is that my the color changes when I set it in Main, but it wont show the Packages from the List.
When i bind the Packages directly to a list in Main.xaml its alright. So the fault has to be somewhere else.
Hope you can help!
the main mistakes which leads to binding erros is unnecessary set DataContext. remove this line from constructor:
DataContext = this;
there is no need to implement INotifyPropertyChanged for UserControl which is also DependencyObject. DependencyProperties have internal mechanism to notify about changes. Remove OnPropertyChanged - declaration and all usages.
Speaking of DependencyProperties: public SolidColorBrush Color { get; set; } doesn't follow required pattern and has to be implemented with GetValue / SetValue method

Binding is not displaying the correct value

Basically, I am reading a value from an xml file and displaying that value in a text box. This text box is editable so the user can make changes to it and when the session ends that value gets stored back in the xml file.
I know that the values correctly get stored and loaded to the text box. But I'm baffled as to why I cannot see the bound value that should be displayed in the text box..
This is the text box:
<UserControl x:Class="test.myView"
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:ui="clr-namespace:test.myView"
xmlns:local="test.myControls;assembly=test.mycontrols">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Style.xaml"/>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel Margin="8">
<TextBlock Text="Starting URL"/>
<TextBox Margin="0,5" FontSize="12" Height="30" Width="360" Text="{Binding myValue, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource WaterMarkTextBox}" local:WaterMarkTextHelper.WatermarkText="ENTER Value" />
</StackPanel>
This is the INotifyPropertyChanged:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaiseChangeNotification(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This is the property im binding to:
public string myValue
{
get { return _settings.myValue; }
set { _settings.myValue= value; }
}
I think it maybe because the value of the text box to empty before the code reaches the part where the start url is set using data binding. Does there exist a validate command in wpf that forces it to view the most up to date value?
There is not enough information to know the exact problem, but there are a few things to check when this sort of things happen.
Check your Output window in Visual Studio. This will give you any binding errors. This will help solve some of the following problems:
Not having your property set to public
Typo with your property name
Not having the DataContext set
Make sure your DataContext implements the INotifyPropertyChanged interface. This is required by the implementation of WPF to update the binding. You would do this like the following:
public class YourDataContext : INotifyPropertyChanged
{
private object _myvalue;
public object myvalue
{
get
{
return _myvalue;
}
set
{
if (_myvalue == value)
return;
_myvalue = value;
OnPropertyChanged("myvalue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
You can also look up several strategies to simplify this, like INotifyPropertyChanged weaving or a BaseViewModel class.
Edit
If myvalue is in your ViewModel that inherits from your ViewModelBase then all you need to do is change the implementation of your property to:
public string myValue
{
get
{
return _settings.myValue;
}
set
{
if (_settings.myValue == value)
return;
_settings.myValue = value;
RaiseChangeNotification("myValue");
}
}
Try to add one dummy converter and see if binding is getting fired when you change value from UI textbox (Convert back method needs to be executed). Else take help of snoop (http://snoopwpf.codeplex.com/) This will help you check status of binding.
And add RaiseNotification in setter if you want to update data from source to target.
public string myValue
{
get { return _settings.myValue; }
set { _settings.myValue= value; RaiseChangeNotification("myValue") }
}

Data Binding problem

I want to do something like this diagram Data Binding Diagram.
If i update TextBox text Then update TextBlock text and Property and if i change Property Value then also update Textbox and textBlock text. Please tell me how can i do it using WPF ????
Thank`s For Help.
Im not sure if I understand right your question. Are the two Textboxes in the same view or in different?
Here a solution with 2 textboxes in the same view:
View (xaml):
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Name="mainWindow">
<StackPanel>
<TextBox Name="UpperTextBox" Text="{Binding ElementName=LowerTextBox, Path=Text,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="LowerTextBox" Text="{Binding MyValue, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
View-Codebehind (xaml.cs):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
}
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _myValue;
public string MyValue
{
get { return _myValue; }
set
{
_myValue = value;
OnPropertyChanged("MyValue");
}
}
}

How to create a UserControl in the MVVM pattern?

Currently, within a real-world application development, I am struggling with the consumption of a custom UserControl in the MVVM pattern.
In my application, there is a DataGrid where the user can select an entry. The DataGrid's SelectedItem is TwoWay-bound to a field of the ViewModel set as DataContext. When the user selects an entry, the field is properly updated (tested). In the Page where holds the DataGrid, the field is bound through XAML to a DependencyProperty of a custom UserControl devised in the MVVM pattern : it bares its own ViewModel which is set as DataContext. The trouble is that the UserControl's DependencyProperty is not updated when the field changes even though the INotifyPropertyChanged interface is correctly implemented (see the comparison with a traditional control in the next minimal working example).
This example is constituted of a Label and bares ViewModelUserControl as a DataContext, UserControl1is consumed by the MainWindow and the binding is compared to that of a Label.
The file MainWindow.xaml:
<Window x:Class="UserControlWithinUserControlDataContext.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Local="clr-namespace:UserControlWithinUserControlDataContext"
Title="MainWindow"
Height="350" Width="525"
>
<StackPanel Orientation="Horizontal"
>
<ListBox SelectedItem="{Binding Text, Mode=TwoWay}"
x:Name="listbox"
Height="150"
>
</ListBox>
<Local:UserControl1 Text="{Binding Text, Mode=OneWay}"
Height="50" Width="150"
/>
<Label Content="{Binding Text, Mode=OneWay}"
/>
</StackPanel>
</Window>
The code-behind MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public ViewModelWindow view_model_window
{
get { return _view_model; }
}
private ViewModelWindow _view_model = new ViewModelWindow();
public MainWindow()
{
InitializeComponent();
DataContext = view_model_window;
IList<String> list = new List<String>();
list.Add("A");
list.Add("B");
list.Add("C");
listbox.ItemsSource = list;
}
}
The ViewModel of the MainWindow, the file ViewModelWindow.cs :
public class ViewModelWindow : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public String Text
{
get { return text; }
set
{
if (text != value)
{
text = value;
NotifyPropertyChanged("Text");
}
}
}
private String text = "Bli";
}
The file UserControl1.xaml:
<UserControl x:Class="UserControlWithinUserControlDataContext.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">
<Grid>
<Label Content="{Binding Text}"
Background="Magenta"
HorizontalAlignment="Stretch"
/>
</Grid>
</UserControl>
The code-behind file UserControl1.xaml.cs:
public partial class UserControl1 : UserControl
{
public ViewModelUserControl view_model_usercontrol
{
get { return _view_model; }
}
private ViewModelUserControl _view_model = new ViewModelUserControl();
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(UserControl1),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(TextPropertyChangedCallback)));
private static void TextPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 user_control = d as UserControl1;
if(user_control != null)
{
user_control.view_model_usercontrol.Text = user_control.Text;
}
}
public UserControl1()
{
InitializeComponent();
DataContext = view_model_usercontrol;
}
}
The ViewModel of UserControl1, the file ViewModelUserControl.cs:
public class ViewModelUserControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public String Text
{
get { return text; }
set
{
if (text != value)
{
text = value;
NotifyPropertyChanged("Text");
}
}
}
private String text = "";
}
As you can see when executing this code, the MainWindow's Label gets updated while the UserControl1's Label doesn't.
What am I doing wrong? Is there a way to makes this works?
Many thanks in advance for any clue.
first you do not need to add anything in the UserControl just the XAML. Remove all the code of the UserControl and try.
Let's explain why:
Content="{Binding Text}" you set this in the usercontrol xaml, it's binded to the ViewModelWindow. and that works. and remove in
<Local:UserControl1 => Text="{Binding Text, Mode=OneWay}"
Ok, but it is correct to define a property in the user control in case of other situation?, that's right, in order to do that:
<UserControl x:Name="UserControlInstance"...>
<Label Content="{Binding Text, ElementName=UserControlInstance}" ...>
Where in this case Text is the dependency property and not the datacontext property.
Try the first option, and then the second defining just a Dependency Property and in this case bind the dependency property as you did.
And a tip, if a dependency property is in the visual element tree like in your case you do not need to call the callback.
Thank you Juan for your answer, here is the solution for conceiving the UserControl in the MVVM pattern:
I gave the name root to the Grid of UserControl1 and set its DataContext:
root.DataContext = view_model_usercontrol;
instead of:
DataContext = view_model_usercontrol;
Everything works fine.
Happy ending :)

Categories

Resources