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>
Related
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.
I have a bunch of textboxes I'm trying to bind to strings in my viewmodel. I thought I had everything set up correctly, but nothing is appearing in the textboxes.
Here's my XAML and one of the textboxes I'm trying to bind.
<Window x:Class="Server.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:l="clr-namespace:Server"
xmlns:viewmodel="clr-namespace:Server.ViewModels"
Title="MainWindow">
<Window.DataContext>
<viewmodel:MainWindowViewModel />
</Window.DataContext>
<TextBlock Name="ShipLatTB"
FontSize="17"
Text="{Binding Path=CurrentShipLat, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Here's the viewmodel:
namespace Server.ViewModels
{
class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _currentShipLat;
public string CurrentShipLat
{
get { return _currentShipLat; }
set { _currentShipLat = value; OnPropertyChanged("CurrentShipLat"); }
}
// 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 tested to make sure data is actually in '_currentShipLat' by setting it equal to "test" in a command, and debugged to verify it. Not sure what else is wrong?
Note: This textbox is supposed to be able to dynamically update.
Edit: How about giving a reason for the downvote and voting to close? That doesn't help anyone.
Make sure you set the field _currentShipLat before the WPF window is initialized.
If you do it after initialisation of the window, WPF will never 'see' this change because it doesn't trigger the property changed event. Either make sure the field is set before the window is initialized or use the setter of the property instead of directly setting the field.
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've stumbled over the following problem. I've got a checkbox whose IsChecked property is bound to a CLR property in my MainWindow class. Here's the source code.
Code behind (MainWindow.xaml.cs):
namespace MenuItemBindingTest {
public partial class MainWindow : Window, INotifyPropertyChanged {
private bool m_backedVariable = false;
public bool IsPressAndHoldEnabled {
get { return this.m_backedVariable; }
set {
this.m_backedVariable = value;
OnPropertyChanged("IsPressAndHoldEnabled");
MessageBox.Show("Item changed: " + this.m_backedVariable);
}
}
public MainWindow() {
InitializeComponent();
this.m_checkbox.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) {
if (this.PropertyChanged != null) {
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
XAML code (MainWindow.xaml):
<Window x:Class="MenuItemBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Binding Problem Test" Width="525" Height="350">
<DockPanel>
<CheckBox x:Name="m_checkbox"
IsChecked="{Binding IsPressAndHoldEnabled}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Content="Is Press and Hold enabled"/>
</DockPanel>
</Window>
The problem now is that the set accessor for the property IsPressAndHoldEnabled is never called (ie. the message box never shows) when the user checks or unchecks the check box. It does, however, work when I rename the property to something else - like IsPressAndHoldEnabled2.
My question now is: Why can't I use IsPressAndHoldEnabled as name for my property? Does this have anything to do with the property Stylus.IsPressAndHoldEnabled existing?
Interesting. I don't have answers why, but I have workarounds:
Seperating the IsPressAndHoldEnabled property out to a seperate ViewModel class worked, unless the class was derived from FrameworkElement.
Also, changing from a regular property to a Dependency Property in the same MainWindow class worked -- the DP changed callback fires.
Have you specified TwoWay as your binding mode? Although I think CheckBox.IsChecked defaults to TwoWay binding mode...
I think you may have messed up your binding context, so that it is not finding the IsPressAndHoldEnabled property. Bindings in WPF fail silently -- a royal pain if you ask me.
Check that the check-box is really bound to that property, and that the binding context really is your MainWindodw class object.
In WPF I have a collection of bool? values and I want to bind each of these to a separate checkbox programmatically. I want the bindings to be TwoWay so that changing the value of the individual item in the collection in code updates the check box and vice versa.
I have spent ages trying to figure out how to do this and I am completely stuck. With the following code the checkbox only gets the right value when the window is loaded and that's it. Changing the check box doesn't even update the value in the collection. (UPDATE: this appears to be a bug in .NET4 as the collection does get updated in an identical .NET3.5 project. UPDATE: Microsoft have confirmed the bug and that it will be fixed in the .NET4 release.)
Many thanks in advance for your help!
C#:
namespace MyNamespace
{
public partial class MyWindow : Window, INotifyPropertyChanged
{
public MyWindow()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public List<bool?> myCollection = new List<bool?>
{ true, false, true, false, true, false };
public List<bool?> MyCollection
{
get { return myCollection; }
set { myCollection = value; }
}
}
}
XAML:
<CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}">
There are a few things that need changing here to get this to work. Firstly you'll need to wrap your boolean value in an object that implements the INotifyPropertyChanged interface in order to get the change notification that you are looking for. Currently you are binding to boolean values in your collection which do not implement the interface. To do this you could create a wrapper class like so :
public class Wrapper: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool val = false;
public bool Val
{
get { return val; }
set
{
val = value;
this.OnPropertyChanged("Val");
}
}
public Wrapper(bool val)
{
this.val = val;
}
}
You'll then want to create these objects in your form instead of a list of booleans. You may also want to use an observable collection instead of a list so that notification of items being added and removed are sent. This is shown below:
public Window1()
{
InitializeComponent();
this.DataContext = this;
}
private ObservableCollection<Wrapper> myCollection = new ObservableCollection<Wrapper>()
{new Wrapper(true), new Wrapper(false), new Wrapper(true)};
public ObservableCollection<Wrapper> MyCollection
{
get { return myCollection; }
}
The next thing to do is to display a list of check boxes in your ui. To do this WPF provides itemscontrols. ListBox is an itemscontrol so we can use this as a starting point. Set the itemssource of a listbox to be MyCollection. We then need to define how each Wrapper object is going to be displayed in the list box and this can be done with a datatemplate which is created in the windows resources. This is shown below :
<Window.Resources>
<DataTemplate x:Key="myCollectionItems">
<CheckBox IsChecked="{Binding Path=Val, Mode=TwoWay}"></CheckBox>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Path=MyCollection}" ItemTemplate="{StaticResource myCollectionItems}"></ListBox>
</Grid>
This should get you up and running with a simple demo of checkboxes that have values bound to a list of booleans.
What makes you think it's not working? It's working for me :)
Here's my test XAML:
<UniformGrid>
<CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}"/>
<ListBox ItemsSource="{Binding MyCollection}"/>
<Button Content="Test" Click="Button_Click"/>
</UniformGrid>
Here's my code behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
}
(the rest is the same as yours)
I placed a breakpoint on Button_Click and checked MyCollection[0] it was updated according to the IsChecked value of the CheckBox.
Try changing your collection type from List<bool?> to ObservableCollection<bool?> perhaps that is the reason you think it's not working for you (the fact that changes to the collection are not reflected anywhere else in your view).
Change your List<bool?> to an ObservableCollection<bool?>. A List does not raise the change notifications that WPF needs to update the UI. An ObservableCollection does. This handles the case where the list entry is changed and the CheckBox needs to update accordingly.
In the other direction, it works for me even with a List<bool?> -- i.e. toggling the checkbox modifies the value in the collection. Your binding syntax is certainly correct.