I have a dependency property of type collection, when its callback fires based on the count I need to set the visibility of some of the controls on the screen.
But the controls remains Collapsed all the time. As per the code, one control remains visible all the time.
XAML binding is
<TextBlock Text="106 search results for 'a'" Margin="5,0,100,0" Visibility="{Binding CountLabelVisibleReverse, Converter={StaticResource VisibilityConverter}}"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,90,0"
Visibility="{Binding CountLabelVisible, Converter={StaticResource VisibilityConverter}}">
<TextBlock Text="Sort By" />
<ComboBox Style="{StaticResource ComboBoxStyle1}" Width="100" x:Name="ComboBoxSorting" ItemsSource="{Binding SortBy}" />
</StackPanel>
My two properties are
public bool CountLabelVisible { get; set; }
public bool CountLabelVisibleReverse { get; set; }
Dependency property callback
private static void ItemsCollectionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs eventArgs)
{
var listingUserControl = (obj as ListingUserControl);
var itemsResult = (eventArgs.NewValue as List<ItemsResult>);
if (listingUserControl != null && itemsResult != null)
{
listingUserControl.CountLabelVisible = itemsResult.Count > 0;
listingUserControl.CountLabelVisibleReverse =itemsResult.Count <= 0;
}
}
Converter code is
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter == null)
return (bool)value == false ? Visibility.Collapsed : Visibility.Visible;
return (bool)value ? Visibility.Collapsed : Visibility.Visible;
}
You have made the classic mistake of binding to auto properties that are valid for binding, but don't notify upon change, which means the binding subsystem cannot detect changes and update the binding targets.
To fix this, implement INotifyPropertyChanged on your viewmodel, and then ensure that you notify the property change from the properties.
As an example, I have the following in the base class for my viewmodels:
public abstract class BaseViewModel : INotifyPropertyChanged
{
/// <summary>
/// Helper method to set the value of a property and notify if the value has changed.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="newValue">The value to set the property to.</param>
/// <param name="currentValue">The current value of the property.</param>
/// <param name="notify">Flag indicating whether there should be notification if the value has changed.</param>
/// <param name="notifications">The property names to notify that have been changed.</param>
protected bool SetProperty<T>(ref T newValue, ref T currentValue, bool notify, params string[] notifications)
{
if (EqualityComparer<T>.Default.Equals(newValue, currentValue))
return false;
currentValue = newValue;
if (notify && notifications.Length > 0)
foreach (string propertyName in notifications)
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Raises the <see cref="E:PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">The name of the property that changed.</param>
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
}
then in your regular viewmodel:
public class MyViewModel : BaseViewModel
{
private bool _countLabelVisible;
public bool CountLabelVisible
{
get { return _countLabelVisible; }
set { SetProperty(ref value, ref _countLabelVisible, true, "CountLabelVisible", "CountLabelVisibleReverse"); }
}
public bool CountLabelVisibleReverse { get { return !_countLabelVisible; }}
}
This way, when CountLabelVisible
gets changed it also notifies on the property CountLabelVisibleReverse
, and the property CountLabelVisibleReverse
consists of only a getter - because it will always be the inverse of CountLabelVisible
.
So that fixes your code the way you have it, but the reality is you don't need to keep the CountLabelVisibleReverse
property, instead you could:
Do your boolean properties that you bind to inform the view when they are changed? Sth like this:
private bool countLabelVisible;
public bool CountLabelVisible
{
get
{
return countLabelVisible;
}
set
{
if (countLabelVisible != value)
{
countLabelVisible = value;
RaisePropertyChanged(() => CountLabelVisible);
}
}
For the method RaisePropertyChanged with lambda to be available, your viewodel should inherit from NotificationObject
You need to notify the change:
public event PropertyChangedEventHandler PropertyChanged;
private bool _countLabelVisible = false;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool CountLabelVisible
{
get
{
return _countLabelVisible;
}
set
{
_countLabelVisible = value;
RaisePropertyChanged("CountLabelVisible");
}
}
The binding "framework" needs to be informed that the binding needs refreshing, which is what the Raise... is about. This is pretty quick and dirty (and untested) but should demonstrate what you need to do.
Bool to visibility converter class
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Xaml Changes mentioned below to show use of visibility converter class. A group box is used here to show the visibility. on change of radio button selection group box will be visible/hidden.
<Page x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication" HorizontalAlignment="Left" VerticalAlignment="Top"
Title="Customer" Loaded="Page_Loaded">
<Page.Resources>
<vm:BoolToVisibilityConverter x:Key="converter"
</Page.Resources>
<RadioButton Grid.Column="0" x:Name="rdbCustomerDetail"
Content="Show Customer"
IsChecked="{Binding IsCustomerDetailChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<GroupBox Header="Customer Details" Visibility="{Binding
Path=IsCustomerDetailChecked,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource converter}}">
</GroupBox>
In ViewModel use invokepropertychange than only you will get the visibility changes on your xaml.
private Boolean isCustomerDetailChecked = false;
public Boolean IsCustomerDetailChecked
{
get
{
return isCustomerDetailChecked;
}
set
{
isCustomerDetailChecked = value;
InvokePropertyChanged("IsCustomerDetailChecked");
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.