How to bind appbar's visibility by code? - c#

How is it possible to set MultiApplicationBarBehavior.IsVisible binding in code?
The problem: if to bind it via xaml, it would be blinking even if binded value is false.
EDIT1: So, what i'm binding to?
<mainPivot:SplashScreenControl
Opacity="1"
Name="SplashScreen"
Visibility="{Binding Opacity, ElementName=SplashScreen, Converter={StaticResource DoubleToVisibilityConverter}}"
/>
<cimbalino:MultiApplicationBarBehavior
SelectedIndex="{Binding PivotIndex}"
IsVisible="{Binding Opacity, ElementName=SplashScreen, Converter={StaticResource DoubleToBooleanInversedConverter}}"
>
Splashscreen: Visibility is binded to Opacity, because Visible object with Opacity=0 is still handling input.
Appbar is just binded to Splashscreen's Opacity. No codebehind at all (just commented out everything). However, appbar is blinking during the page load. That's why i want to set it false by default and later to bind by code.
The only case, when appbar is not blinking is when it is binded to custom property, which is set to false during initialization
<cimbalino:MultiApplicationBarBehavior
SelectedIndex="{Binding PivotIndex}"
IsVisible="{Binding IsSplashScreenVisible, Converter={StaticResource BooleanInversedConverter}}"
>
Converter:
public class DoubleToBooleanInversedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return true;
return !((value as double?) > 0);
}
}

I don't think doing the binding in code will help, did you made sure that the value your are binding to is set to false when the contructor of the page is executed (and that the DataContext is set in the contructor)?
If you are binding to some object proeperty which is null in the contructor you could add FallbackValue=false on the binding.
If you don't find any other solution here is how to create the same binding in code:
Binding binding = new Binding("Opacity");
binding.ElementName = "SplashScreen";
binding.Converter = new DoubleToBooleanInversedConverter();
multiAppBarBehavior.SetBinding(MultiApplicationBarBehavior.IsVisibleProperty, binding);
(where multiAppBarBehavior will be the MultiApplicationBarBehavior control name)

Related

How to keep Two-Way Binding while updating value through Trigger/Setter in ResourceDictionary / Template Document

<Grid x:Name="RedGrid" Visibility="{Binding Path=IsVisible,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
As you can see the Visibility of this Grid is tied to the IsVisible property.
<DataTemplate.Triggers>
<Trigger SourceName="RedGrid" Property="Opacity" Value="0">
<Setter TargetName="RedGrid" Property="Visibility" Value="Collapsed"/>
</Trigger>
</DataTemplate.Triggers>
This part of code will change the visibility of the RedGrid once the opacity reachs / equals zero
But obviously the second part of the code doesn't work, since the Setter for IsVisible is not called when the Visibility of RedGrid is successfully changed to Collapsed.
public Visibility IsVisible
{
get => _isVisible;
set
{
_isVisible = value;
RaisePropertyChanged("IsVisible");
}
}
What you are trying to do is to update the Visibility property of the Grid using a Setter and maintain the binding at the same time. The problem is that binding is overwritten by the Setter!
When you use Xaml Setters you set properties the same way as you set them in the tag, so when you write:
<Setter TargetName="RedGrid" Property="Visibility" Value="Collapsed"/>
It acts the same way as if you've written:
<Grid x:Name="RedGrid" Visibility="Collapsed">
Notice that the binding Visibility="{Binding Path=IsVisible"} has been overwritten by the value Visibility="Collapsed" and will no longer work.
But Why?
The way that TwoWay binding works can be found inside the controls themselves. When you work with DependencyProperties by default you set them using SetValue() method, which sets the property's value and overwrites any binding in place.
However, in controls like TextBox or CheckBox that take in user input, when the user interacts with them, properties like TextBox.Text and CheckBox.IsChecked are being set by the method SetCurrentValue() which like the documentation says:
The SetCurrentValue method changes the effective value of the property, but existing triggers, data bindings, and styles will continue to work.
You can review this question for more information about the differences between SetValue() and SetCurrentVaule() methods.
What to do:
In your case, I think that you can add a GridOpacity property to the view model, and instead of changing the Visibility in Xaml using a Setter, you can change the view model's IsVisible property directly when you set the GridOpacity property.
Here is a sample code of the view model:
private Visibility _isVisible;
public Visibility IsVisible
{
get => _isVisible;
set
{
if (_isVisible == value)
{
return;
}
_isVisible = value;
RaisePropertyChanged(nameof(IsVisible));
}
}
private double _gridOpacity;
public double GridOpacity
{
get => _gridOpacity;
set
{
if (_gridOpacity == value)
{
return;
}
_gridOpacity = value;
RaisePropertyChanged(nameof(GridOpacity));
// Change the visibility of the grid when opacity hits 0.
if (value == 0)
{
IsVisible = Visibility.Collapsed;
}
else
{
// Change it back.
IsVisible = Visibility.Visible;
}
}
}
And here is the Xaml:
<Grid x:Name="RedGrid" Visibility="{Binding IsVisible}" Opacity="{Binding GridOpacity}">
...
</Grid>

WPF Combobox will not send Selection Back to View Model [duplicate]

This question already has answers here:
DataGrid DataGridTemplateColumn ComboBox
(1 answer)
ComboBox SelectedItem property updating all other comboboxes in my Datagrid row
(1 answer)
Closed 2 years ago.
I have a WPF application that has a Combobox that is supposed to be bound to a boolean value. The UI should have a combobox with just two choices to choose from. Here is what I have so far. The XAML:
<DataGridTemplateColumn Header="Instance" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type dataModel:ParameterSetting}">
<Grid Visibility="{Binding IsPlaceholder, Converter={StaticResource BoolToVisInvert}}">
<ComboBox SelectedIndex="{Binding Instance, Mode=TwoWay, Converter={StaticResource ConvBoolToInstance}}">
<ComboBoxItem>Instance</ComboBoxItem>
<ComboBoxItem>Type</ComboBoxItem>
</ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The referenced converter:
[ValueConversion(typeof(bool), typeof(int))]
public class ConvBoolToInstance : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) return 0;
if (!(value is bool e))
throw new ArgumentException(#"Value was not a boolean and could not be converted", nameof(value));
return e ? 0 : 1;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) return true;
if (value == null || !(value is int i))
throw new ArgumentException(#"Value was not an integer and could not be converted", nameof(value));
return i == 0;
}
}
And the property that it's trying to bind to:
/// <summary>
/// If the parameter should be added as instance
/// </summary>
public bool Instance
{
get => _instance;
set
{
if (_instance == value) return;
_instance = value;
OnPropertyChanged(nameof(Instance));
}
}
When I debug this I get the correct choices in the combobox. I can change the default of the field and the combobox displays the correct item, plus I can set a breakpoint on the getter for the property and it's hit so I know that is correctly bound and going to the property I want it to. However, when I change the combobox value it changes the UI but doesn't push back to the property value on the object. I have set break points in both the property setter and the converter and neither are hit. From what I can tell it's just not pushing it back which is what it should do with a two way binding...
What am I missing?
EDIT
The converter is thanks to this answer but I have also tried biding to it as a SelectedValue with boolean combobox items but nothing seems to make it bind back...
EDIT
I should also mention this is inside a DataGrid. Updated question to show more of the XAML
Ok finally figured this out and a bit of a face palm, but posting the answer to help anyone in my same position. The answer was to use UpdateSourceTrigger=PropertyChanged. I believe it defaults to LostFocus which is fine but in this case it was REALLY delayed. Like I selected something in the ComboBox, then clicked into another cell to edit it, then clickout out of THAT cell which apparently finally took focus because THEN it committed. Not sure why it would be so delayed like that but once I switched to PropertyChanged it worked like a charm.
Here is my final binding (after figuring this out I was also able to just use normal binding without the converter):
SelectedItem="{Binding Instance, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

User Control Child elements sharing Value across multiple instances

I have made a User Control, FontSelector, that groups together a ComboBox for FontFamily Selection and three ToggleButtons for Bold, Italics, Underline options. I am having an issue with the ComboBox's SelectedItem property affecting all instances of that User Control within the same Window. For example, changing the ComboBox selection on one, will automatically change the other. For Clarity. I don't want this behavior. I am very surprised that a User Control is implicitly affecting another User Control.
XAML
<Grid x:Name="Grid" Background="White" DataContext="{Binding RelativeSource={RelativeSource AncestorType=local:FontSelector}}">
<ComboBox x:Name="comboBox" Width="135"
SelectedItem="{Binding Path=SelectedFontFamily}" Style="{StaticResource FontChooserComboBoxStyle}"
ItemsSource="{Binding Source={StaticResource SystemFontFamilies}}"/>
</Grid>
Code Behind
The CLR Property that the ComboBox's SelectedItem is Bound to. Code shown here is in the User Control Code Behind File, not a ViewModel.
private FontFamily _SelectedFontFamily;
public FontFamily SelectedFontFamily
{
get
{
return _SelectedFontFamily;
}
set
{
if (_SelectedFontFamily != value)
{
_SelectedFontFamily = value;
// Modify External Dependency Property Value.
if (value != SelectedFont.FontFamily)
{
SelectedFont = new Typeface(value, GetStyle(), GetWeight(), FontStretches.Normal);
}
// Notify.
RaisePropertyChanged(nameof(SelectedFontFamily));
}
}
}
The Dependency Property that updates it's value based on the Value of the ComboBox's SelectedItem Property. It effectively packages the FontFamily value into a Typeface Object.
public Typeface SelectedFont
{
get { return (Typeface)GetValue(SelectedFontProperty); }
set { SetValue(SelectedFontProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedFont. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedFontProperty =
DependencyProperty.Register("SelectedFont", typeof(Typeface), typeof(FontSelector),
new FrameworkPropertyMetadata(new Typeface("Arial"), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnSelectedFontPropertyChanged)));
private static void OnSelectedFontPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = d as FontSelector;
var newFont = e.NewValue as Typeface;
if (newFont != null)
{
instance.SelectedFontFamily = newFont.FontFamily;
}
}
EDIT
I think I may have figured out what is going on. I can replicate it by Binding the ItemsSource to the Following Collection View Source.
<CollectionViewSource x:Key="SystemFontFamilies" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Source"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
You can then replicate the behavior by placing 2 ComboBoxes and Binding both of them to the CollectionViewSource. They will now, seemingly implicitly track each others SelectedItem. Even without Any Data Binding outside of ItemsSource. It would seem that the CollectionViewSource is somehow playing a part in what the SelectedItem is.
I'd make it a bit different. I'll introduce this solution using only a String, not FontFamily or FontWeight, since I have no VS here right now. (In order to have it working, please change the list of FontFamilies to a list of strings to bind them.)
Your selector UserControl:
- your xaml is ok (but you won't need the x:Name)
- the CodeBehind of the UserControl (later: UC) should change, we will solve it with binding. You should have a DependencyProperty, lets' call it SelectedFontFamily, which will represent the selected string from the ComboBox:
public string SelectedFontFamily
{
get { return (string)GetValue(SelectedFontFamilyProperty); }
set { SetValue(SelectedFontFamilyProperty, value); }
}
public static readonly DependencyProperty SelectedFontFamilyProperty = DependencyProperty.Register("SelectedFontFamily", typeof(string), typeof(YourUC), new PropertyMetadata(string.Empty));
The Window, which contains the UC:
- You should include the namespace of the UC's folder in the opening tag of the window, eg:
<Window
...
xmlns:view="clr-namespace:YourProjectName.Views.UserControls">
- the window's DataContext should have a property with public set option (feel free to implement INotifyPropertyChange on it):
public string FontFamily {get; set;}
- in the Window's xaml you would use the UC this way:
<view:YourUC SelectedFontFamily="{Binding FontFamily, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
It's a two-way binding. You'll find the selected string as the value of the FontFamily property every time you change the SelectedItem.
Edit: you will need View Model class for the Window which is using the UserControl. Create it, make it implement the INotifyPropertyChanged interface, and set it as DataContext for your consumer window. WPF is not like WF, you can find more about it if you Google up "WPF MVVM" or something like that.
Found the problem. I was binding to a CollectionViewSource defined in Application Resources. Until now I was unaware that Binding to a CollectionViewSource will also affect the SelectedItem. The SelectedItem Data gets stored as part of the CollectionViewSource. Setting the IsSynchronizedWithCurrentItem property to False on the ComboBox solved the issue.
Here is an existing answer I have now Found.
Thanks

Reverse bool value in binding

I have a TextBlock like this:
<TextBlock Visibility="{Binding IsOnline, Converter={StaticResource boolToVisibilityConverter}}">
boolToVisibility returns Visible if IsOnline is true. But in a situation I want textblock be Collapsed if IsOnline is true.
I can make another converter which acts in reverse, but I want to know isn't it possible to do that in XAML with current converter?
You could potentially use a ConverterParameter value to determine whether to invert the output, for instance:
<TextBlock Visibility="{Binding IsOnline, ConverterParameter=true, Converter={StaticResource boolToVisibilityConverter}}" />
And in the converter itself:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool invertOuput = false;
if (parameter != null) {
bool.TryParse((string)parameter, out invertOuput)
}
// TODO: Converter logic
}
As far as I'm concerned, you'll have to make another converter, although, your converter (not reverese) already exists:
http://msdn.microsoft.com/pl-pl/library/system.windows.controls.booleantovisibilityconverter(v=vs.110).aspx

WPF DataBinding does not update property (just only one time at startup)

I´m having some trouble binding some items attributes.
I have comboboxes and a buttons in a itemscontrol. The combobox is for searching Localities by name. Thats why when the combobox is created, the property IsEditable is true, in order to let the user enter a name, and then press left-control to search that string in the database via WCF.
Then, when the combobox ItemSource.Count is al least 1, I block the combobox by setting IsEditable = false (using the DataBinding of the button). Thats when the button have to change the visibility from hidden to visible, because pressing the button set IsEditable to true again, and ables the user to input a name to search.
To achieve this, I have binded the combobox IsEditable with the button Visibility attribute, and used the following converter, which works:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
public class VisibilityToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (Visibility)value == Visibility.Visible ? false : true;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
As I said, the left control button search the localities, for that I´m using the keydown event:
private void ComboBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl)
{
ComboBox cbx = sender as ComboBox;
LocationServiceClient locationService = new LocationServiceClient();
if (cbx != null)
{
cbx.ItemsSource = locationService.SeachLocalities(new SearchLocalitiesRequest { Search = cbx.Text, MaxItems = 20 }).Localities;
cbx.DisplayMemberPath = "LocalityName";
localityCombobox = cbx;
cbx.IsDropDownOpen = true;
}
}
}
As the Items of the Combobox changed, wouldn´t that have to affect the binding of the Button visibility?
The binding uses this converter, which works too, but only executes once, when I run the app. Thats the problem I´m having, it just does not update the button visibility, and leaves it on Hidden:
public class ItemsSourceCountToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var b = (int)value > 0 ? Visibility.Visible : Visibility.Hidden;
return b;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
This the image of the control, it might help you to get what I say:
Just in case, this is the xaml i used:
<ComboBox Name ="cbxLocality" Width="200" DisplayMemberPath="{Binding LocalityName}" IsEditable="{Binding ElementName= btnRemoveLocality, Path=Visibility, Converter={StaticResource VisibilityToBooleanConverter}}" KeyDown="ComboBox_KeyDown">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding LocalityName}"/>
<TextBlock FontSize="10">
<Run Text="CP: "/>
<Run Text="{Binding ZipCode}"/>
<Run Text=" | "/>
<Run Text="{Binding Province.ProvinceName}"/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Name ="btnRemoveLocality" Content="x" Visibility="{Binding ElementName= cbxLocality, Path=Items.Count, Converter={StaticResource ItemsSourceCountToVisibilityConverter}}" Click="Button_Click_3"></Button>
Does it work to call .DataBind() on cbx when changing ItemSource?
Edit: I would bind the visibility for path Items.Count, instead of just Items, and make the converter handle the integer instead of the Item-list. Because the Count-property triggers the PropertyChanged-event, and the list itself will not if an element is added/removed.
Edit 2: Declare the ObservableCollection of you items as a public property outside the method itself, so it will have public scope. And set it as ItemsSource. Then you won't have to change the ItemSource-property, only the collection itself.

Categories