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.
Related
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.
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;
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);
}
I have been facing a issue in updating the XAML in windows phone 8... the properties are binded in XAML with the viewModel, propertyChange is triggered and it changes the values of the properties. but the property members in XAML are only updated once at the beginning since then it does not update any thing in XAML... Although the properties continue to change in ViewModel.... the properties belong to a LIST of observation collection and finally Observation Collection is binded to LongListSelector
I have changed the binding Mode to "two Way" but useless i have pasted the code below.
Looking forward for help.
ViewModel:
private string _description;
public string description
{
set
{
_description = value;
RaisePropertyChanged("_description");
}
get
{
return _description;
}
}
private double _progress_bar_Value;
public double progress_bar_Value
{
set
{
_progress_bar_Value = value;
RaisePropertyChanged("_progress_bar_Value");
}
get
{
return _progress_bar_Value; //= ProfileSetting.ProfileTab_DOB;
}
}
private double _Total_Bytes;
public double Total_Bytes
{
set
{
_Total_Bytes = value;
RaisePropertyChanged("_Total_Bytes");
}
get
{
return _Total_Bytes;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
`
>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,0" Orientation="Vertical"
>
<TextBlock Text="{Binding description}"
FontSize="18"
TextWrapping="Wrap"
Foreground="White" x:Name="Totalsize"
/>
<ProgressBar x:Name="Download_progressBar"
IsIndeterminate="False"
Maximum="100"
Height="10"
Width="400"
Value="{Binding progress_bar_Value}"
Foreground="White"
/>
<TextBlock Text="{Binding Bytes_received}"
FontSize="18"
TextWrapping="Wrap"
Foreground="White"
x:Name="Total_received"
/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>`
Raise Property Changed on the public property not backing field (as commented by #HighCore)
This must be something obvious, but can anyone tell me why my the value in my label is only updated once. My PropertyChangedEventHandler never fires:
<Page.Resources>
x:Key="SoSummaryViewModelDataSource"/>
</Page.Resources>
<Grid DataContext="{StaticResource SoSummaryViewModelDataSource}">
<Label Grid.Row="2"
Margin="30, 0, 0, 0"
FontWeight="Medium"
Content="{Binding TotalDisplayedCustomers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
Here is my property:
public string TotalDisplayedCustomers
{
get { return _totalDisplayedCustomers; }
set
{
if (_totalDisplayedCustomers != value)
{
_totalDisplayedCustomers = value;
OnPropertyChanged("TotalDisplayedCustomers");
}
}
}
And here is my OnPropertyChanged:
protected void OnPropertyChanged(string propertyName)
{
//when propertyName is TotalDisplayedCustomers, handler is null, why??
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Ha you tried to inspect yout ViewModel load into your DataContext. When i want to inspect it, i use the Wpf Inspector Tools
Well here is what I came up with, I still don't understand what was wrong with my Binding, but instead of relying on the PropertyChanged firing on a string in my view model, I instead bound my Run to the Count property of an ObservableCollection.
First I used a DataContext on my page:
<Page.DataContext>
<vm:SoSummaryViewModel/>
</Page.DataContext>
Changed my TextBlock like this:
<TextBlock Grid.Row="2" Margin="40, 0, 0, 0">
<Run Text="Customer Count: " FontWeight="Medium"></Run>
<Run Text="{Binding SummaryLineItems.Count, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}">
<TextBlock Text=" (Filtered)" Visibility="{Binding HideOnTimeCustomers, Converter={StaticResource showIfTrue}}"/>
</TextBlock>