I have a TextBlock as follow:
<TextBlock Text="You don't have any more items." Visibility="{binding}"
and in code behind I defined a Stack called items as follow:
private Stack<Item> _items;
How do I bind the text visibility in xaml to visible when _item.Any is false?
There are several steps to achieving what you want to do and they are all described here
You need to create a value converter similar to this;
public class EmptyCollectionToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var collection = (Stack<int>) value;
return collection.Any() ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then you need to add a reference to is in your resource dictionary in your xaml like this;
<views:EmptyCollectionToVisibilityConverter x:Key="EmptyCollectionToVisibilityConverter"/>
Finally bind your property in your view model to the visibility of your control and give the binding the converter like this;
Visibility="{Binding Items, Converter={StaticResource EmptyCollectionToVisibilityConverter}}"
Your property will probably need to be an observableCollection (which will mean changing the value converter example I gave you slightly.
I'd probably go with:
private Stack<Item> _items;
// bind to this property using converter
public bool IsVisible => !(_items?.Any(...) ?? false);
You shouldn't expose your _stack directly, but e.g. use methods to do something (because you need to rise notification every time you push/pop an item):
public void PushItem(Item item)
{
_items.Push(item);
OnPropertyChanged(nameof(IsVisible)); // implement INotifyPropertyChanged
}
Related
I have a complex window with various controls that are visible or collapsed based on bool values. I want to add a custom attribute to show all of these controls during design time.
My implementation of the attribute looks like this:
public static class CustomAttributes
{
private static bool? _inDesignMode;
public static readonly DependencyProperty Visibility = DependencyProperty.RegisterAttached(
"Visibility",
typeof(Visibility),
typeof(CustomAttributes),
new PropertyMetadata(VisibilityChanged));
private static bool InDesignMode
{
get
{
if (!_inDesignMode.HasValue)
{
var prop = DesignerProperties.IsInDesignModeProperty;
_inDesignMode =
(bool)DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement)).Metadata.DefaultValue;
}
return _inDesignMode.Value;
}
}
public static Visibility GetVisibility(DependencyObject dependencyObject)
{
return (Visibility)dependencyObject.GetValue(Visibility);
}
public static void SetVisibility(DependencyObject dependencyObject, Visibility value)
{
dependencyObject.SetValue(Visibility, value);
}
private static void VisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!InDesignMode)
return;
d.SetValue(Control.VisibilityProperty, e.NewValue);
}
}
In XAML I use it like this:
<Button Visibility="{Binding SomeBoolValue, Converter={StaticResource BoolToVisibility}}"
helper:CustomAttributes.Visibility="Visible"
/>
However, it does not seem to work. I use some other custom attributes like this and they do their job, but visibility doesn't trigger, it just stays collapsed in the design view. What am I missing?
Edit:
Thank you for pointing me to the right direction. The solution to my problem did not require the custom attribute as I first assumed it would. To achieve the design-time behavior that I wanted, I modified the converter implementation as suggested in the accepted answer below.
Think a little deeper about the logic you created.
The UI element does not have TWO Visibility properties, it is the only one.
But you want to manipulate this property in two ways at the same time: through the binding and the attached property.
Thus, you have created competition between them for this property.
And the property will take on the value that will be assigned to it last.
The attached property will be triggered only once when the Button is initialized (from the example).
And the binding will be triggered when the Data Context and/or its SomeBoolValue property changes.
But the Data Context of the Window is set later than the initialization of the UI elements of this Window.
I see several solutions.
The easiest one, if you need to ALWAYS show elements in Design Mode, is to add the appropriate logic to the converter.
In its simplest form, an example of such a converter:
/// <summary>Bool to Visibility converter.</summary>
[ValueConversion(typeof(bool), typeof(Visibility))]
public class BooleanToVisibilityConverter : IValueConverter
{
public static bool IsDesignMode { get; } = DesignerProperties.GetIsInDesignMode(new DependencyObject());
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool val)
return IsDesignMode || val
? Visibility.Visible
: Visibility.Collapsed;
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I have the an ICollectionView in my ViewModel:
public class ContactsPanelViewModel : ViewModelBase
{
private ICollectionView accountContactsView;
public ICollectionView AccountContactsView
{
get => accountContactsView;
set => NotifyPropertyChangedAndSet(ref accountContactsView, value);
}
public ContactsPanelViewModel()
{
AccountContactsView = CollectionViewSource.GetDefaultView(G.AccountContacts); //G.AccountContacts is an IEnumarable<Contact> object
AccountContactsView.SortDescriptions.Add(new SortDescription("FullName_LastNameFirst", ListSortDirection.Ascending));
AccountContactsView.CurrentChanged += NewContactSelected;
AccountContactsView.MoveCurrentToFirst();
}
}
This is the list it is bound to:
<ListView
x:Name="ContactList"
HorizontalAlignment="Left"
ItemsSource="{Binding Path=AccountContactsView, Converter={StaticResource ContactCardConverter}}"
IsSynchronizedWithCurrentItem="True"/>
And this is the converter I created:
public class ContactCardConverter : IValueConverter
{
public object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is ListCollectionView)
{
List<ContactCard> contactCards = new List<ContactCard>(); //ContactCard is a UserControl
ListCollectionView data = Value as ListCollectionView;
if (data.Count > 0 && data.GetItemAt(0) is Contact)
{
foreach(Contact contact in data)
{
contactCards.Add(new ContactCard(contact));
}
return contactCards;
}
else
{
return null;
}
}
else
{
return null;
}
}
public object ConvertBack(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is ContactCard)
{
return (Value as ContactCard).Contact;
}
else
{
return null;
}
}
}
The issue is that because I am using a converter the CurrentItem in my ICollectionView isn't being synchronized when the ListView selection changes. The converter is definitely the issue since removing the converter makes it work perfectly.
I added in a ConvertBack function thinking it would call that if there was a converter used but it didn't work.
I could theoretically make the ICollectionView of the type ContactCard (which is a UserControl), but that doesn't seem like it is how it should be done since from what I understand the ViewModel shouldn't be dependent on how the View will look. This would also add some complecity as I would need to keep the ICollectionView in sync with my actual collection of Contact objects.
What is the proper way to synchronize a ListView and an ICollectionView when the ListView uses a converter?
The proper way is don't use a converter.
You shouldn't convert your viewmodel-CollectionView to the List(as in your case) or another CollectionView in converter, because otherwise in case you created a CollectionView in converter you will synchronize your view (and IsSynchronizedWithCurrentItem works good) against in converter created collection. List can't be synchronized, because it does not implement IColectionView and has not CurrentItem.
The proper way is don't use a converter and put ContactCard-UserControl to the ListView.ItemTemplate.
<ListView.ItemTemplate>
<DataTemplate>
<youCustomCtlNameSpace:ContactCard Contact="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
If you mandatory wants to use a converter, then IsSynchronizedWithCurrentItem makes no sense and you have to synchronize V and VM by yourself via binding with converter to ListView.SelectedItem and your Contact have to hold reference to the UI-object(It's MVVM conform, if you have no dependencies to View see: Is MVVM pattern broken?). But as I already noted it's not a preferred way!
In my program (MVVM WPF) there are lot of Enumerations, I am binding the enums to my controls in the view.
There are lot of ways to do it.
1) To bind to ComboBoxEdit(Devexpress Control). I am using ObjectDataProvider.
and then this
<dxe:ComboBoxEdit ItemsSource="{Binding Source={StaticResource SomeEnumValues}>
This works fine but in TabControl header it doesn't.
2) So, I thought of using IValueConverter that didnt worked either.
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (!(value is Model.MyEnum))
{
return null;
}
Model.MyEnum me = (Model.MyEnum)value;
return me.GetHashCode();
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return null;
}
on XAML:
<local:DataConverter x:Key="myConverter"/>
<TabControl SelectedIndex="{Binding Path=SelectedFeeType,
Converter={StaticResource myConverter}}"/>
3) The third way of doing this is to make a behavior dependency property
Something like this
public class ComboBoxEnumerationExtension : ComboBox
{
public static readonly DependencyProperty SelectedEnumerationProperty =
DependencyProperty.Register("SelectedEnumeration", typeof(object),
typeof(ComboBoxEnumerationExtension));
public object SelectedEnumeration
{
get { return (object)GetValue(SelectedEnumerationProperty); }
set { SetValue(SelectedEnumerationProperty, value); }
}
I want to know what is the best way to handle enumerations and binding to it. Right now I am not able to bind tabheader to the enums.
Here's a nicer way of doing it:
On your model, put this Property:
public IEnumerable<string> EnumCol { get; set; }
(Feel free to change the name to whatever suits you, but just remember to change it everywhere)
In the constructor have this (or even better, put it in an initialization method):
var enum_names = Enum.GetNames(typeof(YourEnumTypeHere));
EnumCol = enum_names ;
This will take all the names from your YourEnumTypeHere and have them on the property you'll be binding to in your xaml like this:
<ListBox ItemsSource="{Binding EnumCol}"></ListBox>
Now, obviously, it doesn't have to be a ListBox, but now you're simply binding to a collection of strings, and your problem should be solved.
Tearing my hair out here! I have this type-converter:
class CouponBarcodeToVisibilityConverterColumn : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (DesignerProperties.IsInDesignMode)
{
if ((string)parameter == "123456")
{
return Visibility.Visible;
}
return Visibility.Hidden;
}
if (value == null)
{
return Visibility.Visible;
}
var barcodesWanted = ((string)parameter).Split(System.Convert.ToChar("_"));
var actualBarcode = (string)value;
return barcodesWanted.Any(barcodeWanted => barcodeWanted == actualBarcode) ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
I have a UserControl with the following Resources section:
<UserControl.Resources>
<converters:CouponBarcodeToVisibilityConverterColumn x:Key="CouponBarcodeToVisibilityConverter1"/>
</UserControl.Resources>
I have a model called Bet, it looks like this:
public class Bet : INotifyPropertyChanged
{
//Lots of other stuff
private string _barcode;
public string Barcode
{
get { return _barcode; }
set
{
if (value == _barcode) return;
_barcode = value;
OnPropertyChanged("Barcode");
}
}
//Lots of other stuff
}
In the ViewModel which is the DataContext of my user control I have an Observable Collection of Bet. Back to my user control, I have a stack panel, the data context of which is the aforementioned Observable Collection.
Inside the Stack Panel I have a DataGrid, the ItemsSource property is simply {Binding}, deferring the binding up the tree as it were.
Inside my DataGrid I have this column:
<DataGridCheckBoxColumn x:Name="IsEwColumn" Binding="{Binding Wagers[0].IsEw,UpdateSourceTrigger=PropertyChanged}" Header="Each Way" Visibility="{Binding Path=Barcode, Converter={StaticResource CouponBarcodeToVisibilityConverter1}, ConverterParameter=123456}" Width="Auto"/>
The other element of the binding works perfectly (the checkbox is ticked whenever it is supposed to be) but my type converter is not. The breakpoint doesn't even get hit. The Barcode property inside Bet is definitely equal to 123456.
What have I missed?
What you have here is a list of bets for the items source of the data grid.
If you think about it
Bet1 could evaluate to visible when passed via type converter.
Bet2 could evaluate to visible when passed via type converter.
Bet3 could evaluate to collapsed when passed via type converter.
How would the datacolumn be both visible and collapsed at the same time.
You can't bind to visibility like that, unless you had an overall variable on the list or something that it could bind to.
I have a custom command:
public static class CommandLibrary
{
private static RoutedUICommand cmdMyCommand = new RoutedUICommand("My command", "MyCommand", typeof(CommandLibrary));
public static RoutedUICommand MyCommand{ get { return cmdMyCommand ; } }
}
and I register a binding like this
CommandManager.RegisterClassCommandBinding(typeof(SomeClass), new CommandBinding(CommandLibrary.MyCommand, new ExecutedRoutedEventHandler(myCommandExecuteHandler), new CanExecuteRoutedEventHandler(myCommandCanExecuteHandler)));
And in generic.xaml I have a Buton with Command property set. The button is being properly enabled/disabled based on logic in myCommandCanExecuteHandler.
But now I would like to also control this button's visibility (independent of CanExecute which is mapped to IsEnabled). How do I approach that problem?
A discussion about the same problem is available here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/c20782f8-2d04-49d3-b822-f77b4b87c27a/, but somehow the idea that CanBeSeen is a property of RoutedUICommand derived class does not appeal to me.
you can bind the the visibility attribute in xaml to the value which decides button's visibility
<Button Content="Button" Height="23" Visibility="{Binding someclass, Converter={Binding VisibitlityConverter}}"/>
and use a converter to convert bool value to callpsed or visible
class visibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value == true? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Do you want to make the button to be visible when the button is enabled/disabled... If then you have to bind the IsEnabled property to Visibility property using the Boolean to Visibility converter...
I ran into a very similar problem today.
"Sometimes" the CanExecute binding is being ignored when the visibility of the button is set collapsed state by the visibility converter. I said "sometimes" because, if I put a breakpoint in the visibility converter, it alters the behaviour.
When the visibility is changed to Visible - the CanExecute is not being called again. A mouse click anywhere on the UI refreshes the CanExecute binding, which makes it work as expected.
I worked around this issue by binding to the Button IsEnabled property directly to a property on my viewmodel, which reflects what my CanExecute does.