OnPropertyChanged event doesn't toggle Button Visibility - c#

I have a button which only should be active if the given text above it is a valid URL, i got the correct regex and also a OnPropertyChanged method in which i set the button Visibility to true (it gets converted to visibility in the xaml file)...
Although i set the button visibility to true nothing changes
ViewModel Code:
private bool m_isSaveButtonVisible = true;
public bool IsSaveButtonVisible
{
get => m_isSaveButtonVisible;
set
{
m_isSaveButtonVisible = value;
OnPropertyChanged("???"); //i don't know exactly what to call here?
}
}
...
public event PropertyChangedEventHandler PropertyChanged;
protected override void OnPropertyChanged(PropertyChangedEventArgs args)
{
if (MeetingRole == WebRTCMeetingRole.Join)
{
if (Url != m_currentUrl)
{
m_currentUrl = Url;
if (Regex.Match(m_currentUrl, URL_PATTERN, RegexOptions.IgnoreCase).Success)
{
PropertyChanged.Invoke(this, e: args); //should set true
}
else
{
PropertyChanged.Invoke(this, e: args); //should set false
}
}
}
}
XAML Code:
<TextBlock Text="{x:Static p:Resources.webrtc_url}" Foreground="White" FontSize="18" Margin="0 0 0 10"/>
<c:WatermarkTextBox attached:FocusExtension.IsFocused="{Binding IsUrlFocused}"
Foreground="White" FontSize="19" WatermarkForeground="{x:Static co:Colors.Trout}"
Margin="0 0 0 30" Text="{Binding Url, Mode=TwoWay}"
Watermark="{x:Static p:Resources.webrtc_url_hint}" WatermarkHorizontalAlignment="Left" HasFocus="True" SelectAll="True"
EnterCommand="{Binding SaveCommand, Mode=OneTime}" />
...
<c:IconButton Text="{Binding ConfirmButtonText, Mode=OneWay}" TextAlignment="Center" Foreground="White" FontSize="16"
Background="{x:Static co:Colors.DarkOrange}" Margin="0 0 0 8"
Command="{Binding SaveCommand, Mode=OneTime}"
Visibility="{Binding IsSaveButtonVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
Does anybody know why the button visibility isn't set ?
What should happen is, when someone writes a valid URL in the Textfield the savebutton should appear
through the OnPropertyChange i already get noticed when somebody writes something in the textfield the problem is that i cant toggle the button out of this function because it doesn't set the visibility and i don't know why

Property changed just notifies WPF that a property has changed. Nothing more.
so:
public event PropertyChangedEventHandler PropertyChanged;
private bool m_isSaveButtonVisible = true;
public bool IsSaveButtonVisible
{
get => m_isSaveButtonVisible;
set
{
m_isSaveButtonVisible = value;
// if somebody listens to PropertyChanged we tell him IsSaveButtonVisible has changed
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSaveButtonVisible)));
}
}
Should be enough.

Related

Hold state of buttons dont work after binding

I have a UWP application where the buttons hold state work just fine until I bind (any) buttons visibility or editable state where the hold state goes away. The buttons only work if I tap super quickly after something gets binded. If I switch to a different view then back to the original view, the buttons magically work again. Anyone have any ideas what could be causing this?
Added the IsHoldingState="true" to parent and button in xaml.
xaml code
<controls:RoundedButton Grid.Row="2" Style="{StaticResource SubmitButtonStyle}" Command="{Binding SubmitCommand}" VerticalAlignment="Bottom" Visibility="{Binding IsNotEmpty, Converter={StaticResource visibilityConverter}, ConverterParameter=false}" Margin="-16,-16,-32,-32" Width="384" Height="112" Opacity="0" Background="Transparent" />
<controls:RoundedButton Grid.Row="2" Style="{StaticResource SubmitButtonStyle}" Command="{Binding SubmitCommand}" VerticalAlignment="Bottom" Visibility="{Binding IsNotEmpty, Converter={StaticResource visibilityConverter}, ConverterParameter=false}">
cs code (where the binding is updated via onpropertychanged)
private bool isNotEmpty;
public bool IsNotEmpty
{
get { return isNotEmpty; }
set { Set(() => IsNotEmpty, ref isNotEmpty, value); }
}
protected bool Set<T>(Expression<Func<T>> selectorExpression, ref T field, T value)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
RaisePropertyChanged(selectorExpression);
return true;
}
protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
var propertyName = GetPropertyName(selectorExpression);
OnPropertyChanged(propertyName);
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
The problem may be in your ConverterParameter, ConverterParameter can't be directly assigned to a boolean value, please try this:
<Page.Resources>
<x:Boolean x:Key="DefaultParameter">False</x:Boolean>
</Page.Resources>
...
<controls:RoundedButton Visibility="{Binding IsNotEmpty, Converter={StaticResource visibilityConverter}, ConverterParameter={StaticResource DefaultParameter}}">
Best regards.

Binding not updating as expected

I have a pre-loader screen that essentially says "please wait" as I have server-side computation being processed for several seconds.
I have a value converter that should update and get rid of the loader screen once the server-side computation has been processed and stored.
WPF Portion
<Window.Resources>
<Client:BoolToVisibilityConverter x:Key="loadConverter"/>
</Window.Resources>
.
.
.
<Border Panel.ZIndex="1000" BorderBrush="Yellow" BorderThickness="1" Visibility="{Binding OverlayVisibility, Converter={StaticResource loadConverter}, Mode=TwoWay}" Background="#80000000" Margin="0,0,0,-25.6">
<Grid>
<TextBlock Panel.ZIndex="100" Margin="0" TextWrapping="Wrap" Text="Loading Passive Seismic Nodes..." HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="21" FontWeight="Bold" Foreground="#FFF"/>
<TextBlock Panel.ZIndex="100" Margin="11,136,12,75.2" TextWrapping="Wrap" Text="Please Wait..." HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="14" FontWeight="Bold" Foreground="#FFF"/>
</Grid>
</Border
I have an OverlayVisibility property in this class that is a boolean value to help toggle the preloader screen.
Portion of the Client Class
public void LoadRoles()
{
foreach (var roleName in ChefServer.GetCookbookNames())
{
Cookbooks.Add(new Cookbook() { CookbookName = roleName });
}
//This isn't making the preloader disappear
uiContext.Send((_ => { overlayVisibility = false; }), null);
Console.WriteLine("Done!"); //This gets called successfully
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
//This function gets called when WPF form loads
public void Loader()
{
uiContext = SynchronizationContext.Current; //Declared at top in namespace
OverlayVisibility = true; //Make preloader screen show at boot
}
#region Props
private bool overlayVisibility;
public bool OverlayVisibility
{
get { return overlayVisibility; }
set
{
overlayVisibility = value;
OnPropertyChanged("OverlayVisibility");
}
}
#endregion
You're setting overlayVisibility (the field), not OverlayVisibility (the property).
Therefore, you never actually raise PropertyChanged, and WPF never finds out.
Are you sure you have set up DataContext correctly? try adding the following line to your c'tor if you have not set it up yet
this.DataContext = this;

error binding isEnabled to button in xaml

I am new to xaml, WPFs, C# and the MVVM paradigm. I have started with an app based on this example project, in the selected excerpts i want to disable the authenticate button from the LoginPageViewModel after the authenticate button has been clicked(There is no point clicking the button if you are authenticated). I have got command binding working, as well as text control binding between the view and ViewModel. my LoginPageViewModel is based on a abstract class that inherits from INotifyPropertyChanged
The setter AuthenticateButtonEnabled is working, but it is not binding to the isEnabled proprerty on the form. My question is, what could I have missed, and How can i trace the binding between a View and a ViewModel?
the LoginPageView.xaml button:
<Button x:Name="authenticateButton" Content="{x:Static res:Strings.LoginPage_authenticateButton_content}"
Grid.Column="2" Margin="53,4,0,10"
Grid.Row="2" FontSize="16"
IsEnabled="{Binding Path=AuthenticateButtonEnabled}"
Command="{Binding Path=AuthenticateCommand}" HorizontalAlignment="Left" Width="87"/>
the viewModel
private String _username;
private String _responseTextBlock;
private String _linkTextBlockURI;
private String _linkTextBlockText;
private bool _authenticateButtonEnabled;
...
private async void Authenticate()
{
ResponseTextBlock = Strings.LoginPage_responseBlock_content_checking;#this works!
AuthenticateButtonEnabled = false;
return;
}
....
public bool AuthenticateButtonEnabled
{
get { return _authenticateButtonEnabled; }
set { _authenticateButtonEnabled = value; OnPropertyChanged("AuthenticateButtonEnabled"); }
}
// this is in the abstract class.
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
If you want to have both: command and AuthenticateButtonEnabled, then simply check for this property in CanExecute delegate and vise-versa in property setter update command.
Here is implementation with DelegateCommand and some improvements which you may find useful:
bool _isAuthenticateButtonEnabled;
public bool IsAuthenticateButtonEnabled
{
get { return _isAuthenticateButtonEnabled; }
set
{
_isAuthenticateButtonEnabled = value;
OnPropertyChanged();
AuthenticateCommand.Update();
}
}
// the base could class could actually implement this
void OnPropertyChanged([CallerMemberName] string property) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public DelegateCommand AuthenticateCommand { get; }
// view model constructor
public ViewModel()
{
AuthenticateCommand = new DelegateCommand(o =>
{
... // some actions when command is executed
}, o =>
{
bool somecondition = ...; // some condition to disable button, e.q. when executing command
return somecondition && IsAuthenticateButtonEnabled;
});
}
This will let you to have both: property to enable/disable button, which can be used in binding (to another control, e.g. CheckBox.IsChecked) and command which can have independent condition to disable button when command shouldn't be executed (typically in async command delegate, when it performs a long running command, but for this you may want to check this answer.).
if you bind the command Property of the Button to an ICommand Property in your Viewmodel, then you do NOT need to handle the IsEnabled Property of the Button because its handled by the CanExecute Method of the ICommand implementation.
google for RelayCommand or DelegateCommand
Thanks to the posters for your help, I wanted to share the working solution for others. I used the DelegateCommand, but had to change some parts in the loginPageViewModel to make it work: I also updated the xaml so that the controls were all inactive after a successful authentication.
the loginPage xaml:
<Label x:Name="usernameLabel" Content="{x:Static res:Strings.LoginPage_usernameLabel_content}" HorizontalAlignment="Left" Margin="10,4,0,0" Grid.Row="0" VerticalAlignment="Top" Width="130" FontSize="16" Height="36" Grid.Column="1"/>
<TextBox x:Name="usernameTextBox" Grid.Column="2" Grid.Row="0" TextWrapping="Wrap"
Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Margin="10,5,0,6" FontSize="16" HorizontalAlignment="Left" Width="130" TextChanged="usernameTextBox_TextChanged"/>
<Label x:Name="passwordLabel" Content="{x:Static res:Strings.LoginPage_passwordLabel_content}" Margin="10,5,0,0" Grid.Row="1" VerticalAlignment="Top" FontSize="16" Height="36" Grid.RowSpan="2" HorizontalAlignment="Left" Width="130" Grid.Column="1"/>
<PasswordBox x:Name="passwordBox" Grid.Column="2" Margin="10,0,0,9"
PasswordChanged="PasswordBox_PasswordChanged"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Grid.Row="1" FontSize="16" HorizontalAlignment="Left" Width="130"/>
<Button x:Name="authenticateButton" Content="{x:Static res:Strings.LoginPage_authenticateButton_content}"
Grid.Column="2" Margin="53,4,0,10"
Grid.Row="2" FontSize="16"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Command="{Binding Path=AuthenticateCommand}" HorizontalAlignment="Left" Width="87"/>
the loginPageViewModel:
....
private bool _authenticateButtonEnabled;
private DelegateCommand _authenticateCommand;
public bool AuthenticateButtonEnabled {
get { return _authenticateButtonEnabled; }
set
{
_authenticateButtonEnabled = value;
DynamicOnPropertyChanged(); // this is so named to not content with onPropertyChanged defined elsewhere.
AuthenticateCommand.Update();
}
}
...
public DelegateCommand AuthenticateCommand
{
get {
if (_authenticateCommand == null)
{
_authenticateCommand = new DelegateCommand(Authenticate, AuthenticateEnded);
}
return _authenticateCommand;
}
}
private bool AuthenticateEnded(object obj) {
return _authenticateButtonEnabled;
}
private async void Authenticate(object obj)
{
AuthenticateButtonEnabled = false;
ResponseTextBlock = Strings.LoginPage_responseBlock_content_checking;
i3SoftHttpClient _httpClient = new i3SoftHttpClient();
i3SoftUser _i3SoftUser;
AuthenticateCommand.CanExecute(false);
....
// if authentication does not succeed - turn the buttons back on.
AuthenticateCommand.CanExecute(true);
}
and to the Delegate command class i added:
public void Update()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}

Set textbox value when another change in a listview

I have an ObservableCollection containing Hour (object). Inside, I have a Title and a Value properties.
On my view, I have a listview, binding on this collection. Title is a textblock, Value is a textbox (user can input text).
I would like to change the content of all textbox (value) when one change.
A litle bit of code :
public class Hour : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public string Title { get; set; }
private int valueContent;
public int Value
{
get { return valueContent; }
set
{
valueContent = value;
NotifyPropertyChanged("Value");
}
}
}
my observablecollection :
private ObservableCollection<Hour> hours;
public ObservableCollection<Hour> Hours
{
get { return hours; }
set
{
hours= value;
NotifyPropertyChanged("Hours");
}
}
xaml :
<ListBox Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="3" Grid.RowSpan="3" ItemsSource="{Binding Hours, Mode=TwoWay}" SelectedItem="{Binding SelectedHour,Mode=TwoWay}" ItemTemplate="{StaticResource HourTemplate}" />
<DataTemplate x:Key="HourTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" FontSize="18" Width="150" />
<TextBox Text="{Binding Value, Mode=TwoWay}" FontSize="15" Width="150" TextChanged="TextBox_TextChanged" />
</StackPanel>
</DataTemplate>
So, I will have for example :
Title - Value
08h00 - 0
09h00 - 0
10h00 - 0
11h00 - 0
12h00 - 0
I would like, when I change one value (e.g: 10h00), all value after this one change to the value of 10h00.
Here the result expected :
Title - Value
08h00 - 0
09h00 - 0
10h00 - 1 <--- change here
11h00 - 1 <--- change because 10h00 changed
12h00 - 1 <--- change because 10h00 changed
Thanks for your help.
There isn't any clean way to do this.
I would start by adding an event to the Hour class, ValueUpdated. Raise that event in the setter for Value and have the view model listen to it for every Hour object. Have the event pass the sender as a parameter, something like:
public event Action<Hour> ValueUpdated;
//When raising
var handler = ValueUpdated;
if (handler != null)
handler(this);
Now in the view model handler, you need to find the index of the sender, then apply the change to every hour after it.
private void HandleValueUpdate(Hour sender)
{
int senderIndex = allItems.IndexOf(sender);
IEnumerable<Hour> subsequentHours = allItems.Skip(senderIndex + 1);
foreach (Hour h in subsequentHours)
{
h.SetValue(sender.Value);
}
}
You'll probably want to make a way of doing that set without raising the ValueUpdated event, as this won't be very efficent if you do. I modeled that by calling a function instead of setting the property, but how you do it is up to you.

Open a WPF Popup with a button

I'm trying to open a Popup with a button and have implemented a basic ICommand. The button is binded to the ICommand OpenPopupCommand while the Popup IsOpen attribute is binded to the "IsOpen" OnPropertyChanged. My thought process was to bind the Popup.IsOpen attribute to the ICommand as well to have IT trigger the OnPropertyChange but couldn't get it to work. I think I'm close but can't figure it out. Here is the code I have so far:
#region ICommand Members
private ICommand _openPopupCommand;
public ICommand OpenPopupCommand
{
get
{
if (_openPopupCommand == null)
_openPopupCommand = new RelayCommand(param => OpenPopupExecute(param));
return _openPopupCommand;
}
set
{
_openPopupCommand = value;
}
}
#endregion
#region Methods
public void OpenPopupExecute(object parameter)
{
parameter = true;
OnPropertyChanged("IsOpen");
}
#endregion
Button that "pops up" the Popup and the Popup XAML:
<Popup x:Name="FieldsPopup" Placement="Center" Width="400" Height="250" IsOpen="{Binding IsOpen}">
<StackPanel>
<TextBlock Background="LightBlue" HorizontalAlignment="Center" VerticalAlignment="Center" Height="250" Width="350" TextAlignment="Center" >This is a popup</TextBlock>
</StackPanel>
</Popup>
<Button Name="button_PatientIdentifierList" Width="23" Height="23" Grid.Column="2" Foreground="Black" Background="#FFCDCDCD" BorderBrush="#FF707070" Margin="3.4,4,4,0" VerticalAlignment="Top" Command="{Binding OpenPopupCommand}"/>
You're raising the PropertyChange notification, but I don't see you actually changing the property anywhere.
Unless I'm mistaken, this code here takes the CommandParameter (called parameter here) and sets it to true
public void OpenPopupExecute(object parameter)
{
parameter = true;
OnPropertyChanged("IsOpen");
}
However in your XAML the Button.CommandParameter isn't bound to anything
<Button Command="{Binding OpenPopupCommand}"/>
So I suspect that parameter is just null, and is not actually doing anything here.
What you seem to be missing is the actual IsOpen property definition, and setting it to true in your command's Execute code :
private bool _isOpen;
public bool IsOpen
{
get
{
return _isOpen;
}
set
{
_isOpen = value;
OnPropertyChanged("IsOpen");
}
}
public void OpenPopupExecute(object parameter)
{
IsOpen = true; // Will call OnPropertyChanged in setter
}
As a side note, I really don't like WPF's default PopupControl, and have a custom UserControl version of it on my blog if you ever decide you hate WPF's default PopupControl too :)

Categories

Resources