In my WPF program. I have a DataModel.cs, MainWindow.xaml & MainWindow.xaml.cs and many UserControl.xaml & UserControl.xaml.cs which are integrated into MainWindow.xaml in this way:
<Border...>
<UserControl1/>
</Border>
<Border...>
<UserControl2/>
</Border>
...
I am not sure if it can be called MVVM?
Some of my UserControl.xaml.cs need to use the same string for connection which should be typed on the UI textbox somewhere like this:
DataModel.connection.Connect("[textbox.text]");
My question is where do I put this textbox so that each UserControl can get access to it and how? Thanks.
You create a string property in your DataModel class:
public class DataModel : INotifyPropertyChanged
{
private string _text;
public string Name
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged(nameof(Name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
...and bind the TextBox in the view to this string property:
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
Provided that you set the DataContext of the window to an instance of your DataModel class:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new DataModel();
}
}
...the source property (Text) will be updated whenever you type something into the TextBox. If you let the UserControls inherit the DataContext from the parent window, i.e. don't set the DataContext of the UserControls explicitly somewhere, they can bind to the very same Text property and get the latest value "automatically" as the DataModel implement the INotifyPropertyChanged interface and raise the PropertyChanged event.
This is roughly how basic MVVM works.
I think you need read more on MVVM, short answer, as long as you are not referencing and UI elements directly in you ViewModel, you can consider it MVVM.
There are multiple ways you can do it, I would define and interface with
public interface IProvideTextProperty {string SomeText{get;set;}}
Implement this interface in all UserControls that use this property
ON the UI have a textbox in MainWindow, Bind the textproperty of this TextBox to SomeText(viewmodel of MainWindow also implements IProvideTextProperty), in the setter, loop though all the viewmodels that are IProvideTextProperty and set the SomeProperty.
Related
I'm currently developping an universal app in C#/XAML with MVVM (not MVVM Light) and I have trouble for the XAML part.
I'd like to display one or another StackPanel when a dependency property changed in my ViewModel. I think the code speaks for itself.
<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- Some content -->
</StackPanel>
<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource InvertBooleanToVisibilityConverter}}">
<!-- Some another content -->
</StackPanel>
And here is the definition of the dependency property.
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
"MyProperty",
typeof (bool),
typeof (MyViewModel),
new PropertyMetadata(true));
public bool MyProperty
{
get { return (bool) GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); OnPropertyChanged(); // Implemented by ReSharper }
}
I guess you figure it out that MyProperty is a boolean that I convert into a Visibility via the converters. So, when MyProperty changed in the ViewModel, the view isn't updated.
I already tried to use the UpdateSourceTrigger property but it's not working. Also, I have no binding error and converters are working fine (I only see one StackPanel at the app launch).
Please keep in mind that I don't want to use the code behind part unless there is no other solution.
Thanks for your help.
I finaly gave up and used the code behind part and it's working fine now.
Are your <StackPanel>s part of some UserControl? If not, why are you using DependencyProperty?
Your implementation is quite off as well.
Lets assume for a minute that this is not part of a Custom Control (correct me -- if I'm wrong, I will rewrite the solution)
So you have a ViewModel and you want to hook up some Properties to it. You really don't need to implement DependencyProperty to do what you want to do, but I will entertain you by implementing it your way.
This is a sample ViewModel with 1 (one) property
using Windows.UI.Xaml;
using System.ComponentModel;
// very simple view model
class MyViewModel : DependencyObject, INotifyPropertyChanged
{
// implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// register
public static DependencyProperty FooterTitleProperty = DependencyProperty.Register("FooterTitle", typeof(string), typeof(MyViewModel),
new PropertyMetadata(string.Empty, OnFooterTitlePropertyChanged));
// the actual property
public string FooterTitle
{
get { return (string) GetValue(FooterTitleProperty); }
set
{
SetValue(FooterTitleProperty, value);
}
}
// this will fire when the property gets change
// it will call the OnPropertyChanged to notify the UI element to update its layout
private static void OnFooterTitlePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
MyViewModel mvm = dependencyObject as MyViewModel;
mvm.OnPropertyChanged("FooterTitle");
}
}
To test out the code we will make a very simple XAML form
<Grid x:Name="ContentPanel">
<StackPanel>
<TextBlock x:Name="tb" Text="{Binding FooterTitle}" FontSize="48"></TextBlock>
<Button Content="Test Property" Click="Button_Click_1"></Button>
</StackPanel>
</Grid>
When you click on the button we will change the Textbox's Text
public sealed partial class MainPage : Page
{
// create the view model
MyViewModel vm = new MyViewModel();
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
// set the text we initial want to display
vm.FooterTitle = "default text";
// set the DataContext of the textbox to the ViewModel
tb.DataContext = vm;
}
// after the button is click we change the TextBox's Text
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// change the text
vm.FooterTitle = "Test Property Has Changed.";
// what happens is the Setter of the Property is called first
// after that happens it launches the `OnFooterTitlePropertyChanged` event
// that we hook up with the Register function.
// `OnFooterTitlePropertyChanged` launches the INotifyPropertyChanged event
// then finally the TextBox will updates it's layout
}
}
At this point you can guess you really don't need the DependencyProperty and say why can't I just launch the INotifyPropertyChanged in the Setter instead? Well you can and it is probably the prefer method.
If all these is part of a UserControl then I can see using a DependencyProperty then in the OnFooterTitlePropertyChanged event you can set the
name_of_textbox.Text = FooterTitle;
I think property name should be given with OnPropertyChanged method, like this;
public bool MyProperty
{
get { return (bool) GetValue(MyPropertyProperty); }
set {
SetValue(MyPropertyProperty, value);
OnPropertyChanged("MyProperty");
}
}
https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.inotifypropertychanged.propertychanged
I need to know in the simplest form, how to bind data (list of string) to a ComboBox in XAML without using ComboBox.ItemSource = object in the code behind.
I mean what is this:
{Binding Path="What Comes here"}
let's say I have:
class Window1 : UserControl {
List<String> list = new List<String>();// And assign Value to this list
...}
I've tried
{Binding Path=list} or {Binding list}
but nothing has been bind. So how should it be done? (I can't access this combobox in the code behind because Microsoft has limited SilverLight DatGrid to an extent that I can't do it)
Few rules for binding to work properly:
You can bind only with public properties (at least for instance objects) and not with fields.
Be default binding engine looks for property path in DataContext of control where binding is applied on.
If you want to bind to property which doesn't exist in DataContext (or DataContext is not set for control), use RelativeSource markup extension to guide binding engine to resolve property path.
Coming back to your problem statement where you need to bind to List created in code behind, you have to do following changes to code:
// Not sure why you termed it as Window1 if it's actually an UserControl.
public partial class Window1 : UserControl
{
public Window1()
{
InitializeComponent();
MyStrings = new List<string>(new[] { "A", "B", "C", "D" });
DataContext = this; // Rule #2
}
public List<string> MyStrings { get; set; } // Rule #1
}
XAML:
<ComboBox ItemsSource="{Binding MyStrings}"/>
In case you don't set DataContext in constructor of UserControl and still want to bind to property, bind using RelativeSource (Rule #3)
<ComboBox ItemsSource="{Binding MyStrings, RelativeSource={RelativeSource FindAncestor
, AncestorType=UserControl}}"/>
Additional Points
Use ObservableCollection<string> in place of List<string> in case you want to add more items to the list after initialization and want UI to update accordingly.
Read more about MVVM here - Understanding the basics of MVVM pattern. Generally all binding stuff stay in separate class ViewModel so that it can be tested w/o any dependency on UI stuff.
Take a look over here: Model-View-ViewModel (MVVM) Explained
I'm not a SilverLight developer but as far as I know it's just a downstiped version of full wpf.
Try using a ViewModel and define your list in there. Then you can bind the ComboBox.ItemsSource property to it:
public class SomeViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> listOfAwesomeStrings;
public ObservableCollection<string> ListOfAwesomeStrings
{
get { return listOfAwesomeStrings; }
set
{
if (value.Equals(listOfAwesomeStrings))
{
return;
}
listOfAwesomeStrings= value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Set the DataContext of your view like this and also fill your list:
var viewModel = new SomeViewModel();
viewModel.ListOfAwesomeStrings = new ObservableCollection<string>();
viewModel.ListOfAwesomeStrings.Add("Option 1");
viewModel.ListOfAwesomeStrings.Add("Option 2");
viewModel.ListOfAwesomeStrings.Add("Option 3");
this.DataContext = viewModel;
Finally bind to your property in xaml:
<ComboBox ItemsSource="{Binding ListOfAwesomeStrings}" />
I have some issues with OnPropertyChanged.
On my View, I wrote:
<StackPanel>
<Label Content="{Binding TestProperty, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Click me" Click="Button_Click" />
</StackPanel>
This Data Context is:
class MainWindowViewModel : PropertyChangedBase
{
public string TestProperty
{
get
{
if (TestWrapperModel.Instance.TestModel == null)
return "Test Initial String";
return TestWrapperModel.Instance.TestModel.TestProperty;
}
}
}
My TestWrapperModel is just a simple singleton wrapper that implements PropertyChangedBase.
My TestModel is like:
class TestModel : PropertyChangedBase
{
private String _testProperty= "";
public String TestProperty
{
get
{
return _testProperty;
}
set
{
_testProperty= value;
OnPropertyChanged("TestProperty");
}
}
}
PropertyChangedBase is an abstract class that implements INotifyPropertyChanged interface.
Whenever I change the value of TestProperty, OnPropertyChanged fires, but the label on the UI doesn't show the new value (So it always display "Test Initial String"). Snoop says the binding works.
I think it's a simple problem and I just can't see what the problem is. Do you have any idea?
You need to propagate the event from the child viewmodel to the parent viewModel.
Subscribe to the PropertyChanged event of TestModel and raise a further PropertyChanged event in your MainWindowViewModel class to notify the view that the TestProperty property has changed.
It won't work, since you DataContext is your MainWindowViewModel class, and not your TestModel class. When the application starts, it gets the value from TestProperty only once, and if doesn't change it won't change the label's value.
You should write the set on your TestProperty on the MainWindowViewModel to change your label.
The PropertyChanged event fires on the model, but you are binding on the viewmodel. If the latter doesn't invoke its own PropertyChanged event the binding engine doesn't know that things have changed.
I'm trying to convert my console app to a nice WPF GUI. Am getting a little stuck on this code and was wondering if someone can help?
In my xaml I have this:
<CheckBox IsChecked="{Binding CL.LoggedIn}"></CheckBox>
to try and bind the value of the checkbox to the value of CL.LoggedIn. CL is my ConnectionLibrary.cs class in a referenced class library.
In the code behind for the xaml page i declare CL as follows :
public ConnectionLibrary CL = new ConnectionLibrary();
In the connection library class I have added :INotifyPropertyChanged to the class declaration and added the following code:
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
I have changed the LoggedIn property to now look like this:
private bool loggedIn;
public bool LoggedIn {
get { return loggedIn; }
set { loggedIn = value; OnPropertyChanged("LoggedIn"); }
}
However, it doesnt seem to work in my xaml? I dont get any binding errors in the output window, but it doesnt reflect the value of LoggedIn correctly.
Any ideas?
Thanks!
Have you set the datacontext of your view?
In the code-behind of your XAML file, you need to do:
this.DataContext = CL;
then the binding is:
<CheckBox IsChecked="{Binding LoggedIn}"></CheckBox>
The binding will find the the named path (i.e. LoggedIn) on the object that is in the DataContext.
EDIT: The default binding is one-way, this means it only gets updated from your ViewModel.
For controls that can be inputed data (i.e: TextBox, CheckBox...) you can set the Binding as "TwoWay". The Binding expression becomes:
<CheckBox IsChecked="{Binding LoggedIn, Mode="TwoWay"}"></CheckBox>
Now whenever the Checked state changes in the UI, it is reflected in your ViewModel.
When you use Binding like this, it binds to the current DataContext, not to the page itself.
The easiest way to fix this would be to set DataContext = this at the end of the constructor of the page.
The proper way to fix it would be to use MVVM. That would mean having ConnectionLibrary in a property of another class and set the DataContext to this other class.
<CheckBox IsChecked="{Binding LoggedIn}"></CheckBox>
I like to create a UserControl with own Header Property.
public partial class SomeClass: UserControl, INotifyPropertyChanged
{
public SomeClass()
{
InitializeComponent();
}
private string header;
public string Header
{
get { return header; }
set
{
header = value;
OnPropertyChanged("Header");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
in UserContol xaml:
Label Name="lbHeader" Grid.Column="0" Content="{Binding Path=Header}"
If I set the value: AA2P.Header = "SomeHeeaderText"; than the label.Caption will not changed. How can I solve that problem?
In Windows xaml:
uc:SomeClass x:Name="AA2P"
If I give directly a value to label (lbHeader.Content = header;) instead of OnPropertyChanged("Header"); its work but, why it does not work with OnPropertyChanged?
I need to use DataContext for somethig else. I try to use dependency property but something is wrong.
public partial class tester : UserControl
{
public tester()
{
InitializeComponent();
}
public string Header
{
get { return (string)GetValue(MyDependencyProperty); }
set { SetValue(MyDependencyProperty, value); }
}
public static readonly DependencyProperty MyDependencyProperty =
DependencyProperty.Register("MyDependencyProperty", typeof(string), typeof(string));
}
<UserControl ... x:Name="mainControl">
<TextBlock Text="{Binding ElementName=mainControl, Path=MyDependencyProperty}"/>
</UserControl>
<Window ...>
<my:tester Header="SomeText" />
</Window>
It does not work. What I do wrong?
Thanks!
The easiest approach is to just the DataContext of your object. One way of doing that is directly in the constructor like this:
public SomeClass()
{
InitializeComponent();
DataContext = this;
}
Setting the DataContext will specify where new data should be fetched from. There are some great tips and information in the article called WPF Basic Data Binding FAQ. Read it to better understand what the DataContex can be used for. It is an essential component in WPF/C#.
Update due to update of the question.
To my understanding you should change the first argument of DependencyProperty.Register to the name of the property that you want to bind to, here "Header" as well as the second argument to the type of your class, here SomeClass. That would leave you with:
public static readonly DependencyProperty MyDependencyProperty =
DependencyProperty.Register("Header", typeof(SomeClass), typeof(string));
But i seldom use dependency properties so I am not positive that this is it, but its worth a try..
If you need the Data context for something else. You can also utilize the ElementName property in the Binding.
<UserControl
x:Class="MyControl.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="mainControl">
<TextBlock Text="Binding ElementName=mainControl, Path=MyDependencyProperty}"/>
</UserControl>
[Edit]
I should add something. Make the "Header" property a dependency property, this will make your live much easier. In UI Controls you should make property almost always a dependency property, every designer or user of your control will thank you.
The UserControl itself needs the DataContext of where it is used later. But the controls inside the UserControl need the UserControl as their DataContext, otherwise they also will inherit the DataContext from the later usage context. The trick is to set the DataContext of the UserControl's child to that of the UserControl, so it now can use the dependency properties of the UserControl.
<UserControl x:Class="MyControl.MyUserControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=UserControl,AncestorLevel=1}}">...</Grid>
</UserControl>
If you do this this way the children of the Grid can have simple {Binding dp's name} without additionally ElementName parameters.