I have created a simple Silverlight user control and defined a public property AllowMultiple.
public bool AllowMultiple { get; set; }
Now, I am setting this public property in XAML as follows:
<Controls1:PeopleChooser Name="SinglePeopleChooser" AllowMultiple="False" Width="Auto" d:LayoutOverrides="Height"/>
<Controls1:PeopleChooser Name="MultiplePeopleChooser" AllowMultiple="True" Width="Auto" d:LayoutOverrides="Height"/>
I want to know, which is the best event I can get the value of this public property. I am trying to get this value inside of the constructor and trying to hide/show some controls inside my user controls but its not working.
public PeopleChooser()
{
InitializeComponent();
if (AllowMultiple)
{
UsersListBox.Visibility = System.Windows.Visibility.Visible;
UserTextBox.Visibility = System.Windows.Visibility.Collapsed;
ResolveButton.Visibility = Visibility.Collapsed;
}
else
{
UsersListBox.Visibility = System.Windows.Visibility.Collapsed;
UserTextBox.Visibility = System.Windows.Visibility.Visible;
ResolveButton.Visibility = Visibility.Visible;
}
}
Probably because during constructor initialization the value of this public property has not been assigned by framework to the object.
I was able to solve it through loaded event. There is no need for dependency property. Please see the code below. I can access the properties value successfully in Loaded event.
public PeopleChooser()
{
this.Loaded += PeopleChooser_Loaded;
InitializeComponent();
}
void PeopleChooser_Loaded(object sender, RoutedEventArgs e)
{
if (AllowMultiple)
{
UsersListBox.Visibility = System.Windows.Visibility.Visible;
UserTextBox.Visibility = System.Windows.Visibility.Collapsed;
ResolveButton.Visibility = Visibility.Collapsed;
}
else
{
UsersListBox.Visibility = System.Windows.Visibility.Collapsed;
UserTextBox.Visibility = System.Windows.Visibility.Visible;
ResolveButton.Visibility = Visibility.Visible;
}
Convert the public property with a backfield,
private bool _allowMultiple;
public bool AllowMultiple
{
get { return _allowMultiple; }
set { _allowMultiple = value; }
}
Place a break point in the setter and check is it hits on Constructor, if not you can use the Loaded event to check the same and make use of that.
If you use a dependency property, you can bind other elements properties to the AllowMultiple property of the people chooser and use a visibility converter to show/hide them. Example:
public partial class PeopleChooser : UserControl
{
public PeopleChooser()
{
InitializeComponent();
}
public static readonly DependencyProperty AllowMultipleProperty = DependencyProperty.Register("AllowMultiple", typeof(bool), typeof(PeopleChooser), null);
public bool AllowMultiple
{
get { return (bool)GetValue(AllowMultipleProperty); }
set { SetValue(AllowMultipleProperty, value); }
}
}
<UserControl x:Class="TestSilverlightApplication.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:lcl="clr-namespace:TestSilverlightApplication">
<UserControl.Resources>
<lcl:VisibilityConverter x:Key="VisibilityConverter" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Vertical">
<Button Click="Button_Click" Content="Toggle allow multiple" />
<lcl:PeopleChooser x:Name="lclPeopleChooser" AllowMultiple="False"></lcl:PeopleChooser>
<TextBlock Text="Dependent content" Visibility="{Binding AllowMultiple, ElementName=lclPeopleChooser, Converter={StaticResource VisibilityConverter}}" />
</StackPanel>
</Grid>
</UserControl>
private void Button_Click(object sender, RoutedEventArgs e)
{
lclPeopleChooser.AllowMultiple = !lclPeopleChooser.AllowMultiple;
}
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool parsedValue = false;
bool.TryParse(value.ToString(), out parsedValue);
if (parsedValue)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This way you can avoid page events and potentially bind the AllowMultiple property to a view model property and let the UI take care of itself.
Related
I'm trying to bind the Visibility property of a FontIcon to an enum property in my view model using a converter, but for some reason it throws an exception
Unable to cast object of type 'Windows.UI.Xaml.Controls.FontIcon' to type
'Windows.UI.Xaml.Data.Binding'
What I want to achieve is that depending on the current value of CurrentSortOrder hide or show an icon inside the MenuFlyoutItem
View model code:
public class TestViewModel : ViewModelBase
{
private TaskSortType _currentTaskSortOrder = TaskSortType.BY_NAME_ASC;
public TaskSortType CurrentSortOrder
{
get => _currentTaskSortOrder;
set => Set(ref _currentTaskSortOrder, value);
}
}
View:
<Page
x:Class="UWPTests.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:UWPTests.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:UWPTests"
xmlns:localModels="using:UWPTests.Models"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{x:Bind ViewModel}"
mc:Ignorable="d">
<Page.Resources>
<converters:TaskSortTypeToVisibilityConverter x:Key="TaskSortTypeToVisibilityConverter" />
</Page.Resources>
<Grid>
<AppBarButton Icon="Sort" Label="Sort">
<AppBarButton.Flyout>
<MenuFlyout>
<MenuFlyoutSubItem Text="By name">
<MenuFlyoutItem Text="Asc">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="" Visibility="{Binding CurrentSortOrder, Mode=OneWay, Converter={StaticResource TaskSortTypeToVisibilityConverter}, ConverterParameter={x:Bind localModels:TaskSortType.BY_NAME_ASC}}" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Desc">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="" Visibility="Collapsed" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
</MenuFlyoutSubItem>
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
</Grid>
Converter:
public class TaskSortTypeToVisibilityConverter : IValueConverter
{
public Visibility OnTrue { get; set; }
public Visibility OnFalse { get; set; }
public TaskSortTypeToVisibilityConverter()
{
OnFalse = Visibility.Collapsed;
OnTrue = Visibility.Visible;
}
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is null || parameter is null)
return Visibility.Collapsed;
var currentOrder = (TaskSortType)value;
var targetOrder = (TaskSortType)parameter;
return currentOrder == targetOrder ? OnTrue : OnFalse;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is Visibility == false)
return DependencyProperty.UnsetValue;
if ((Visibility)value == OnTrue)
return true;
else
return false;
}
}
Any help would be appreciated
Edit:
I get the exception here: this.InitializeComponent();
public sealed partial class MainPage : Page
{
public TestViewModel ViewModel { get; set; }
public MainPage()
{
ViewModel = new TestViewModel();
this.InitializeComponent();
}
}
Edit 2:
public enum TaskSortType
{
BY_NAME_ASC = 0,
BY_NAME_DESC = 1,
BY_UPDATED_DATE_ASC = 2,
BY_UPDATED_DATE_DESC = 3,
}
It seems that i cant use x:Bind directly in the ConverterParameter .. So i ended with the following:
I added in my page resources:
<localModels:TaskSortType x:Key="TaskSortByNameAsc">BY_NAME_ASC</localModels:TaskSortType>
<localModels:TaskSortType x:Key="TaskSortByNameDesc">BY_NAME_DESC</localModels:TaskSortType>
<localModels:TaskSortType x:Key="TaskSortByUpdatedDateAsc">BY_UPDATED_DATE_ASC</localModels:TaskSortType>
<localModels:TaskSortType x:Key="TaskSortByUpdatedDateDesc">BY_UPDATED_DATE_DESC</localModels:TaskSortType>
And then i replaced the ConverterParameter binding with the following:
<FontIcon Glyph="" Visibility="{Binding CurrentSortOrder, Mode=OneWay, Converter={StaticResource TaskSortTypeToVisibilityConverter}, ConverterParameter={StaticResource BY_NAME_ASC}}" />
Another workaround would be to pass the corresponding value in the ConverterParameter, for example ConverterParameter=0 or ConverterParameter="BY_NAME_ASC"and the cast that parameter to the corresponding enum value
in my app i have ColorToBrushConverter.cs, ColorItem.cs and a box page which contain some collection of colors when user click on any of color and back to mainpage it save to settings isolated storage then i able to set my stackpanel any any element background to choosed color from that colorbox page.
But Problem is i have a style in which i want color binding so can we do it from c# or use color binding in xaml from below class.
ColorToBrushConverter.cs
namespace CustomColorsPicker.Converters
{
public class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
return new SolidColorBrush((Color)(value));
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
ColorItem.cs
namespace ColorBox
{
public class ColorItem
{
public Color Color { get; set; }
}
}
BoxPage.Xaml
contains list of color
xmlns:converters="clr-namespace:CustomColorsPicker.Converters"
<Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
</Page.Resources>
//////////
<ListBox Grid.Row="2" Name="listBox" ScrollViewer.VerticalScrollBarVisibility="Disabled" SelectionChanged="lstColor_SelectionChanged" Width="460" Height="770" Margin="0,20,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="item" Orientation="Horizontal" Margin="10,10,0,0">
<Border CornerRadius="5" BorderThickness="2" BorderBrush="{Binding Color, Converter={StaticResource ColorToBrushConverter}}">
<Rectangle Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}" Width="50" Height="50" />
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
BoxPage.xaml.cs
//Constructor. list of colors
static uint[] uintColors =
{
0xFFD9325D,
0xFFFFFF00,0xFFFFE135,0xFFFFFF66,0xFFF8DE7E,0xFF008000,0xFF008A00
};
public BoxPage()
{
InitializeComponent();
this.Loaded += BoxPage_Loaded;
}
private async void BoxPage_Loaded(object sender, RoutedEventArgs e)
{
List<ColorItem> item = new List<ColorItem>();
for (int i = 0; i < 67; i++)
{
item.Add(new ColorItem() { Color = ConvertColor(uintColors[i])});
};
listBox.ItemsSource = item;
}
private void lstColor_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
(Application.Current as App).CurrentColorItem = ((ColorItem)e.AddedItems[0]);
}
}
MainPage.xaml.cs
//Constructor
IsolatedStorageSettings ColourSettings = IsolatedStorageSettings.ApplicationSettings;
public MainPage()
{
InitializeComponent();
InitializeSettings();
}
private void InitializeSettings()
{
if (!ColourSettings.Contains("LastColorItem"))
{
ColorItem item = new ColorItem();
item.Color = Colors.Cyan;
ColourSettings.Add("LastColorItem", item);
}
}
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
ColourSettings["LastColorItem"] = _colorItem;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (ColourSettings.Contains("LastColorItem"))
{
_colorItem = (ColorItem)ColourSettings["LastColorItem"];
}
ColorItem myColorItem = (Application.Current as App).CurrentColorItem;
if (myColorItem != null)
{
_colorItem = (ColorItem)myColorItem;
}
MyFillStackPanel.Background = new SolidColorBrush(_colorItem.Color);
MyCtrlPanelBorder.Background = new SolidColorBrush(_colorItem.Color);
}
MainPage.xaml
xmlns:converters="clr-namespace:CustomColorsPicker.Converters"
<Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
</Page.Resources>
In One of my style i want to bind it with above color because i am unable to do or edit style in c#
//SomeStyle
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="{**i want to bind color here**}"/>
</DiscreteObjectKeyFrame.Value>
Assuming your converter is working correctly, what is actually missing in your code is the actual Binding process.
Your ColorItem class (which needs to derive from the interface INotifyPropertyChanged) has to declare the PropertyChanged event.
When your property Color gets modify you want to raise an event, so the UI gets notified that the property Color has been updated.
You do that on convention by calling a method with the same name as your eventhandler prefixed by "On", therefore you would have to implement the method
OnPropertyChanged, which as I've mentioned would be responsible for actually raising the PropertyChanged event.
There are many ways from which you can define this implementation, but you can look here to see an implementation from Microsoft themselves.
enter link description here
Expose your property,
public ColorItem MyColor {get;set;}
so when you define your {Binding ...} the CLR will be able to find the property during Runtime.
In the MainPage constructor, you can initialize this property
MyColor = new ColorItem();
And define the DataContext of the page as:
this.DataContext = MyColor;
Now you should be able to have your source update the target with the code which you have defined. If you intend to have your UI to propagate modifications onto the source you have to define the Binding with Mode=TwoWay, since the default mode for Binding is Mode=OneWay
Edit
public class ColorItem: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public Color color
{
get
{
return _color;
}
set
{
_color = value;
this.OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Expose the property and set it as the DataContext of your page.
Then reference it in the Binding by {Binding MyColor.color .... }
Preface
The control I am giving as an example is an sample work for a larger project. I have already had some help from the community on Stackoverflow ironing out some of the finer points of bindings within the control. The surprise has been that I am having an issue binding in the control's hosting form.
I have read and researched around DependencyProperty for a lot of hours. I was not a WPF developer at the start of the year but I am now covering the role because of a death in the business, and I accept this is a big hill to climb.
The question is what is missing here in my:
The hosting form's XAML code
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:AControl="clr-namespace:AControl;assembly=AControl" x:Class="DependencySampler.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, Mode=OneWayToSource}"/>
</Grid>
The code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new viewModelBinding();
BeSelected = new modelMain("Yellow", "#FFFFE0");
}
public modelMain BeSelected
{
get { return ((viewModelBinding)DataContext).Selected; }
set { ((viewModelBinding)DataContext).Selected = value; }
}
}
The ViewModel
public class viewModelBinding :ViewModelBase
{
modelMain sel = new modelMain("Red", "#FF0000");
public modelMain Selected
{
get { return sel; }
set { SetProperty(ref this.sel, value, "Selected"); }
}
}
The next section is the control itself.
The Model
public class modelMain:ViewModelBase
{
public modelMain(string colName, string hexval)
{
ColorName = colName;
HexValue = hexval;
}
string colorName;
public string ColorName
{
get { return colorName; }
set { SetProperty(ref this.colorName, value, "ColorName"); }
}
string hexValue;
public string HexValue
{
get { return hexValue; }
set { SetProperty(ref this.hexValue, value, "HexValue"); }
}
}
The ViewModel
public class viewModelMain:ViewModelBase
{
ObservableCollection<modelMain> val = new ObservableCollection<modelMain>();
public ObservableCollection<modelMain> ColorsList
{
get { return val; }
set { SetProperty(ref this.val, value, "Colors"); }
}
modelMain selectedColor;
public modelMain SelectedColour
{
get{return selectedColor;}
set { SetProperty(ref this.selectedColor, value, "SelectedColour"); }
}
public void SetCurrentColor(modelMain col)
{
SelectedColour = this.val.Where(x => x.ColorName == col.ColorName).FirstOrDefault();
}
public viewModelMain()
{
val.Add(new modelMain("Red", "#FF0000"));
val.Add(new modelMain("Blue", "#0000FF"));
val.Add(new modelMain("Green", "#008000"));
val.Add(new modelMain("Yellow", "#FFFFE0"));
SelectedColour = new modelMain("Blue", "#0000FF");
}
}
The UserControl XAML
<UserControl x:Class="AControl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="32" d:DesignWidth="190">
<Grid>
<ComboBox x:Name="cboValue"
SelectionChanged="cboValue_SelectionChanged"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding ColorList, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedValue="{Binding SelectedColor, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="10"
Height="10"
Margin="5"
Background="{Binding ColorName}"/>
<TextBlock Width="35"
Height="15"
Margin="5"
Text="{Binding ColorName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
The UserControl Code behind
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
ObservableCollection<modelMain> colorList = new viewModelMain().ColorsList;
public ObservableCollection<modelMain> ColorList
{
get { return colorList; }
set { colorList = value; }
}
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(
"SelectedColor",
typeof(modelMain),
typeof(UserControl1),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnSelectedColorChanged),
new CoerceValueCallback(CoerceSelectedColorCallback)));
private static void OnSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 uc = (UserControl1)d;
uc.SelectedColor = (modelMain)e.NewValue;
}
private static object CoerceSelectedColorCallback(DependencyObject d, object value)
{
return (modelMain)value;
}
public modelMain SelectedColor
{
get { return (modelMain)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
private void cboValue_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var dat = sender as ComboBox;
SelectedColor = (modelMain)dat.SelectedValue;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
//var dat = sender as ComboBox;
////SelectedColor = (modelMain)dat.SelectedValue;
//SelectedColor = (modelMain)this.SelectedColor;
}
}
Please note that in the code behind there is unused code but within the sample I have used then for placing break points
I understand that no DataContext should exist in the UserControl because it precludes one in the hosting form.
The Question
I was expecting the this line would be sufficient in the hosting form.
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, Mode=OneWayToSource}"/>
But it does not seem to do what I expected. I can see the BeSelected be initialised and it is holding a value but when the form loads I am expecting the colour yellow to enter the UserControl's and set DependencyProperty SelectedColor. This is not happening why and how can I get it to happen?
To get you example working, do the following (for the most part, implement what the commenters said):
The hosting form's XAML code
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, RelativeSource={RelativeSource AncestorType=Window}}}"/>
The Mode doesn't really matter since MainWindow doesn't implement INPC nor does it ever know when ((viewModelBinding)DataContext).Selected (and therefor, BeSelected) is changed. Actually, like Joe stated, OneWayToSource doesn't work... RelativeSource was needed because BeSelected is a property of the MainWindow - not MainWindow's DataContext.
modelMain
modelMain needs to implement IEquatable (like Janne commented). Why? Because BeSelected = new modelMain(...) creates a new modelMain which is not one of the items in the ComboBox's ItemsSource (ColorList). The new object may have the same property values as one of the items but that doesn't make them equal (different objects = different address in memory). IEquatable gives you the opportunity to override that.
public class modelMain : ViewModelBase, IEquatable<modelMain>
{
...
public bool Equals(modelMain other)
{
return (HexValue == other.HexValue);
}
}
viewModelMain's ColorList's setter is calling SetProperty with property name "Colors" when it should be "ColorsList". It's not being used so it doesn't stop your example from working but it's still wrong.
I have a UserControl and I am loading it dynamically in a ContentControl on a Button click. I have a TextBlock in the UserControl and I want to show some text dynamically (which is basically a status which my another method will return while processing a request) in the TextBlock after the UserControl is loaded. I tried setting up this in a Loaded event of the UserControl but the text is already there when the UserControl is fully loaded and showed.
Can someone please give an idea about how to achieve this. I checked this and this link but none seems to be working for me.
Thanks Deepak
If your loading is logical rather than physical you must handle it yourself.
Particularly you must have a way to tell the UserControl when the data are loaded.
Here is a full sample using MVVM (not perfect):
The UserControl:
<UserControl x:Class="WpfApplication1.LoadDataView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="boolToVizConverter"></BooleanToVisibilityConverter>
<local:ReverseBooleanToVisibilityConverter x:Key="reverseBoolToVizConverter"></local:ReverseBooleanToVisibilityConverter>
</UserControl.Resources>
<Grid>
<TextBlock Visibility="{Binding Model.IsLoadingData,Converter={StaticResource boolToVizConverter}}">...</TextBlock>
<TextBlock Visibility="{Binding Model.IsLoadingData,Converter={StaticResource reverseBoolToVizConverter}}">Data loaded</TextBlock>
</Grid>
</UserControl>
So "..." is displayed while we are loading (in a real application you would use a better UI control to render this status like a ProgressRing).
And when the data are loaded we display "Data loaded".
Code behind:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class LoadDataView : UserControl
{
public LoadDataViewModel Model
{
get { return (LoadDataViewModel)GetValue(ModelProperty); }
set { SetValue(ModelProperty, value); }
}
public static readonly DependencyProperty ModelProperty = DependencyProperty.Register("Model", typeof(LoadDataViewModel), typeof(LoadDataView));
public LoadDataView()
{
InitializeComponent();
this.DataContext = this;
}
}
}
The view model:
using System.ComponentModel;
namespace WpfApplication1
{
public class LoadDataViewModel : INotifyPropertyChanged
{
private bool isLoadingData = false;
public bool IsLoadingData
{
get { return isLoadingData; }
set
{
if (value != isLoadingData)
{
isLoadingData = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsLoadingData"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
}
Here is the main view:
<StackPanel>
<ContentControl x:Name="content"></ContentControl>
<Button Click="Button_Click">Display data</Button>
</StackPanel>
When we click the Button we display the data UserControl and we trigger the loading:
private async void Button_Click(object sender, RoutedEventArgs e)
{
LoadDataViewModel model = new LoadDataViewModel { IsLoadingData = true };
content.Content = new LoadDataView { Model = model };
await Task.Delay(3000);
model.IsLoadingData = false;
}
The async/await and Task stuff is just there to simulate the loading of the data.
Finally here is the reverse bool to visibility converter:
using System;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication1
{
public class ReverseBooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I have a boolean property (that does called INotifyPropertyChanged in the setter) that is bound to a button.IsEnabled property in my XAML. Currently I'm using a TwoWay binding, but this is causing problems and I only need a OneWay binding. My problem is that the converter I'm using doesn't get called beyond the first time the program starts up. I've put breakpoints in the setter and it gets called loads, but the Convert() method doesn't get called at all. Why is this?
Some code:
public bool IsSaving
{
get
{
return _isSaving;
}
set
{
_isSaving = value;
NotifyOfPropertyChange(() => IsSaving);
}
}
and the XAML:
IsEnabled="{Binding Path=IsSaving, Mode=OneWay, Converter={StaticResource booleanToNotEnabledConverter}}"
The converter really just returns !(bool)value so the button gets disabled when IsSaving is true.
Some changes at runtime might cause the binding to break (since you bind to the DataContext + a relative path), if you use Visual Studio make sure to check the Output-window for any binding errors.
Edit: Since it has not been noted: That is a stardard binding and there is nothing wrong with the posted code, the problem has to be caused by the context.
Here is the code I used and this works:
Converter:
using System.Windows.Data;
using System;
namespace SilverlightApplication1
{
public class BooleanToNotEnabledConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
XAML:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightApplication1"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.Resources>
<local:BooleanToNotEnabledConverter x:Key="booleanToNotEnabledConverter" />
</UserControl.Resources>
<StackPanel Margin="100">
<Button Content="Flip"
Click="Button_Click" />
<TextBlock Text="{Binding IsSaving}"
Height="20" />
<Button IsEnabled="{Binding IsSaving, Mode=OneWay, Converter={StaticResource booleanToNotEnabledConverter}}"
Content="Some Button" />
</StackPanel>
</UserControl>
Code behind:
using System.Windows.Controls;
using System.Windows;
using System.ComponentModel;
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
private Data _data;
public MainPage()
{
InitializeComponent();
_data = new Data { IsSaving = true };
this.DataContext = _data;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_data.IsSaving = !_data.IsSaving;
}
}
public class Data : INotifyPropertyChanged
{
#region IsSaving Property
private bool _isSaving;
public bool IsSaving
{
get
{
return _isSaving;
}
set
{
if (_isSaving != value)
{
_isSaving = value;
OnPropertyChanged("IsSaving");
}
}
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Are you sure you invoke the PropertyChanged event handler with the correct string?
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsSaving"));