UWP Binding two booleans to IsEnabled property - c#

I have two different objects instantiated in my UWP program. Their class definitions look like this:
namespace StackOverflow
{
public class FirstClass : INotifiyPropertyChanged
{
private bool isEnabled = false;
public bool IsEnabled
{
get => isEnabled;
set
{
isEnabled = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class SecondClass : INotifiyPropertyChanged
{
private bool isEnabled = false;
public bool IsEnabled
{
get => isEnabled;
set
{
isEnabled = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The binding on the XAML side currently looks like this:
<Button Content="Press me if you can" IsEnabled="{x:Bind FirstClass.IsEnabled, Mode=OneWay}"/>
With this code, Button.IsEnabled is only dependent on FirstClass.IsEnabled, what I'd like to achieve is for the Button.IsEnabled property to be dependent on FirstClass.IsEnabled AND SecondClass.IsEnabled. How can I obtain such a result?
I wanted to implement MultiBinding, but it seems to be a WPF-only feature

We can't bind two properties directly in xaml.It seems to only be able to operate in code-behind.When you set the IsEnabled of FirstClass or SecondClass,then judge the IsEnabled of Button manually.

Related

Implement onPropertyChanged on property in all pages

I'm binding an activity indicator to a property called IsLoading to show that the page is busy (e.g. processing API calls). I need to implement this in all my MAUI app pages, so my question, how can I re-use this code? The property is implemented as follows:
public event PropertyChangedEventHandler PropertyChanged;
private bool isLoading;
public bool IsLoading
{
get => isLoading;
set
{
isLoading = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
You probably want to put this in a base class that you inherit from. For example.
Create a class/file BaseViewModel.cs, of course the name can be whatever you want. It might look like this:
public class BaseViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isLoading;
public bool IsLoading
{
get => isLoading;
set
{
isLoading = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now, whenever you create a new view model, you need to inherit from this. Let's say that you create a EditPersonViewModel, that would look like this:
public class EditPersonViewModel : BaseViewModel // This is where you inherit from the BaseViewModel
{
private bool isSaved;
public bool IsSaved
{
get => isSaved;
set
{
isSaved = value;
OnPropertyChanged();
}
}
}
Now you have access to all the things that are also in BaseViewModel. So you can set IsLoading to true or false, or you can implement a new property, IsSaved in my above example, and you can still call OnPropertyChanged to make the UI aware of the value change.

Listen to global variable change in XAML

I have a global variable indicating if my app is in read-only mode
public static class Global
{
public static event PropertyChangedEventHandler StaticPropertyChanged;
private static void OnStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
private static bool _isReadOnly = false;
public static bool IsReadOnly
{
get { return _isReadOnly; }
set
{
_isReadOnly = value;
OnStaticPropertyChanged("IsReadOnly");
}
}
}
Now I would like to listen to that in all my GUIs to turn off editing. For instance I have a DataGrid
<UserControl xmlns:models="clr-namespace:MyApp.Models" >
<DataGrid IsReadOnly="{Binding Path=(models:Global.IsReadOnly)}" />
</UserControl>
How can I listen to the global variable instead of a local one in my ViewModel? Currently I get the error message
The name Global does not exist in the namespace Models.
But it does! And I already tried to recompile and restart VS.
Instead of using a static property, you could use a singleton implementation. Than you have an instance and may implement INotifyPropertyChanged:
public class Global : INotifyPropertyChanged
{
private Global() { }
public Global Instance { get; } = new Global();
private bool _isReadOnly;
public bool IsReadOnly
{
get => _isReadOnly;
set
{
if (_isReadOnly != value)
{
_isReadOnly = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(IsReadOnly)));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then you can use it like:
<DataGrid IsReadOnly="{Binding Source={x:Static local:Global.Instance}, Path=IsReadOnly}" />
As mentioned by Clemens in a comment, since .Net 4.5 there is a static PropertyChanged event that also works with static properties:
public static event PropertyChangedEventHandler StaticPropertyChanged;

Windows 8.1 UI not updating when MVVM property set is called by UI element

My problem is, that the UI isn't updating if they call the setter of the property which they binded to.
Here's a sample to make it clear:
Let's say I have a textbox binded to a property like this.
<TextBox PlaceholderText="Task Name..." FontSize="24"
Text="{Binding TaskName, Mode=TwoWay}" />
And this is my property:
public string TaskName
{
get
{
return _taskName;
}
set
{
_taskName = "something";
RaisePropertyChanged();
}
}
If I write something into the textbox then "something" should appear inside of it, after it loses focus, but there isn't any change. However, if I change the value of the property with code, like this:
TaskName = "something";
Then the change will appear on the UI as well.
Some further information.
This is how I implemented the INotifyPropertyChange interface:
public class ViewModelBase : INotifyPropertyChanged
{
public static Navigator NavigationService;
public static void SetNavigationService(Navigator service)
{
NavigationService = service;
}
protected void GoBack()
{
NavigationService.GoBack();
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I really don't know why is it behave like this. I search for it for hours, but can't find anything.
in the setter of the property you need to call
RaisePropertyChanged(x => x.TaskName)

Binding variable to a textblock in Windows Phone 8

In XAML, i have a textblock
<TextBlock x:Name="block" Text="{Binding b1}"/>
and in c# i created a property
public int _b1;
public int b1
{
get { return _b1; }
set
{
_b1 = value;
}
}
public MainPage()
{
InitializeComponent();
block.DataContext = this;
}
this worked fine, textblock show the _b1. But when i add a button to chage the _b1 variable
private void bt_click(object sender, RoutedEventArgs e)
{
_b1 = 4;
}
the textblock didn't update ?????
To add to dotNet's answer (which is the correct answer), use a baseclass where you implement INotifyPropertyChanged if you want to avoid redundand code: (this is one example, there are other ways to implement this)
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value)) { return false; }
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And use it like so:
class MyClass: BindableBase
{
private int _b1;
public int B1
{
get { return _b1; }
set { SetProperty(ref _b1, value); }
}
}
For UI to update automatically upon property value change, your property needs to either be a DependencyProperty or your class needs to implement INotifyPropertyChanged interface.
For creating a DependencyProperty, you could use Visual Studio's propdp snippet (type propdp inside your class and press Tab) and fill in respective values. If you want to go INotifyPropertyChanged path, you'll need to write the following code in the setter of your property (AFTER setting the value of _b1):
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("b1"));

XAML Binding to property

I have check box in my XAML+C# Windows Store application. Also I have bool property: WindowsStoreTestApp.SessionData.RememberUser which is public and static.
I want check box's property IsChecked to be consistent (or binded, or mapped) to this bool property.
I tried this:
XAML
<CheckBox x:Name="chbRemember1" IsChecked="{Binding Mode=TwoWay}"/>
C#
chbRemember1.DataContext = SessionData.RememberUser;
Code for property:
namespace WindowsStoreTestApp
{
public class SessionData
{
public static bool RememberUser { get; set; }
}
}
But it doesn't seem to work. Can you help me?
<CheckBox x:Name="chbRemember1" IsChecked="{Binding Path=RememberUser, Mode=TwoWay}"/>
public class SessionData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
bool _rememberUser;
public bool RememberUser
{
get
{
return _rememberUser;
}
set
{
_rememberUser = value;
NotifyPropertyChanged("RememberUser");
}
}
}
You need to implement some form of change notification in order for your check box to be "aware" of any changes to the property. The best bet is to use one of the many MVVM frameworks out there, if not, implement INotifyPropertyChanged in your ViewModel.
Also, typically in WPF, we do not set the DataContext of individual controls but set the DataContext of the Window or User Control to a ViewModel...
Here is an example of a property with change notification through one of the MVVM frameworks:
private bool createTrigger;
public bool CreateTrigger
{
get { return createTrigger; }
set { createTrigger = value; NotifyPropertyChanged(m => m.CreateTrigger); }
}
As you can see a simple auto-implemented property cannot be used for data binding in WPF...
I'd recommend going through Data Binding Overview over on MSDN...
You cannot bind to static properties as static properties cannot raise the PropertyChanged event. You will, of course, need INotifyPropertyChanged. But that is not relevant with static properties. You simply cannot bind to static properties. (You can in WPF and Silverlight)
Try like this, note that the property is not static but the backing field is:
public class SessionData : INotifyPropertyChanged
{
private static bool _rememberUser;
public event PropertyChangedEventHandler PropertyChanged;
public bool RememberUser
{
get { return _rememberUser; }
set
{
_rememberUser = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
this.DataContext = new SessionData();
<CheckBox x:Name="chbRemember1" IsChecked="{Binding RememberUser, Mode=TwoWay}"/>

Categories