In WPF project I use MVVM pattern.
I try to bind an item in a collection to UserControl but everything gets default value of DependcyProperty.
The Window xaml:
<ListView VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
ItemsSource="{Binding Sessions}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="DebugTextBlock" Background="Bisque"
Text="{Binding Connection}"/>
<usercontrol:SessionsControl Model="{Binding Converter=
{StaticResource DebugConverter}}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Where Sessions is
private ObservableCollection<SessionModel> _sessions;
public ObservableCollection<SessionModel> Sessions
{
get { return _sessions; }
set
{
if (Equals(value, _sessions)) return;
_sessions = value;
OnPropertyChanged("Sessions");
}
}
The SessionModel:
public class SessionModel:ViewModelBase
{
private string _connection;
public string Connection
{
get { return _connection; }
set
{
if (value == _connection) return;
_connection = value;
OnPropertyChanged("Connection");
}
}
}
In the SessionsControl I create DependencyProperty:
//Dependency Property
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(SessionModel),
typeof(SessionsControl), new PropertyMetadata(new SessionModel("default_from_control")));
// .NET Property wrapper
public SessionModel Model
{
get { return (SessionModel)GetValue(ModelProperty); }
set { if (value != null) SetValue(ModelProperty, value); }
}
and use this xaml to display connection in form:
<TextBlock Name="DebugControlTextBlock" Background="Gray" Text="{Binding Connection}"/>
So, when I run application
var windowModel = new WindowsModel();
var window = new SessionWindow(windowModel);
window.ShowDialog();
I always get default_from_control value in DebugControlTextBlock, but in DebugTextBlock get the_real_connection
Even if I set breakpoint in DebugConverter I see that value is default.
The DebugConverter is simply wrapper to check correct binding:
public class DebugConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("DebugConverter: " + (value!=null?value.ToString():"null"));
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
See solution on github.
So, what happend when I Binding model to DependcyProperty?
I would suggest that you try to make you Sessions property a DependencyProperty too. Otherwise you might have to manually Raise PropertyChanged for Sessions property.
Related
i have a problem converting a string to a icon. The icon Geometry is in a ResourceDictionary. The ValueConverter is not called (i tried to debug in the Convert Method of the Converter). Here is my code:
xaml:
<Window.Resources>
<local:StatusToPathDataConverter x:Key="PathConverter"/>
</Window.Resources>
<Grid>
<Path Width="20"
Height="20"
Stretch="Uniform"
Fill="Black"
Data="{Binding Path=Status,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource PathConverter}}"/>
</Grid>
cs:
public partial class MainWindow :Window {
public MainWindow() {
InitializeComponent();
}
public string Status
{
get { return (string)GetValue(StatusProperty); }
set { SetValue(StatusProperty, value); }
}
public static readonly DependencyProperty StatusProperty =
DependencyProperty.Register("Status", typeof(string), typeof(MainWindow));
}
public class StatusToPathDataConverter :IValueConverter {
private static ResourceDictionary iconDictionary;
public ResourceDictionary IconDictionary
{
get
{
if(iconDictionary == null) {
iconDictionary = new ResourceDictionary();
iconDictionary.Source = new Uri("/WPFBindingTest;component/Resources/IconDictionary.xaml", UriKind.RelativeOrAbsolute);
}
return iconDictionary;
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var status = (string)value;
if(statinStatus == null)
return null;
switch(status.ToLower()) {
case "test":
return IconDictionary["TestIcon"];
// ...
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
You're not binding to anything. You need to tell the Binding to go to the Window to find the Status property. The recommended way to do that is to use RelativeSource, as below:
<Path
Width="20"
Height="20"
Stretch="Uniform"
Fill="Black"
Data="{Binding Path=Status,
RelativeSource={RelativeSource AncestorType=Window},
Converter={StaticResource PathConverter}}"
/>
As #Clemens notes, UpdateSourceTrigger=PropertyChanged doesn't make any sense on this binding and should not be there. That attribute tells the Binding when it should update the binding's source property. The source property is Window.Status, in this case.
However, the Path.Data property does not update the property it's bound to. A Path displays a Geometry; it doesn't edit a Geometry. UpdateSourceTrigger exists for control properties that update viewmodel properties, like TextBox.Text. That's the most common use for UpdateSourceTrigger=PropertyChanged: By default TextBox.Text updates the source property when the TextBox loses focus, but sometimes you want it to update on each keystroke.
Set the DataContext of the window to itself for the binding to work and the Convert method of the converter to get called:
public MainWindow() {
InitializeComponent();
DataContext = this;
}
If the binding to the source property fails the converter will never be invoked.
<Window.Resources>
<local:WeightConverter x:Key="weightConverter" RequiredUnit="{Binding VmProp}" />
<TextBlock Text="{Binding Weight, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource weightConverter}}" />
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
In the MyViewModel I have regular property
private string vmProp;
public string VmProp
{
get
{
return "kg";
}
}
And Convertor class has DependencyProperty is:
public class WeightConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty RequiredUnitProperty = DependencyProperty.Register("RequiredUnit", typeof(string), typeof(WeightConverter), null);
public string RequiredUnit
{
get
{
return (string)this.GetValue(RequiredUnitProperty);
}
set
{
this.SetValue(RequiredUnitProperty, value);
}
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double dblValue;
if (double.TryParse(value.ToString(), out dblValue))
{
if (this.RequiredUnit == "kg")
{
return dblValue;
}
else
{
return dblValue * 10;
}
return dblValue;
}
return 0;
}
When I do binding in XAML the code works:
<Window.Resources>
<local:WeightConverter x:Key="weightConverter" RequiredUnit="kg"/>
But when I try to bind it to ViewModelProperty the 'RequiredUnit' object is always null.
How can I bind dependency property to ViewModel property?
The reason its null is because you are trying to bind to the view model property from the resources, but the datacontext is not available to the resource. In your output log, you must be getting a binding expression error. Have a look at the output window.
There are multiple ways to get this working.
One way is to give your window a name like x:Name="MainWindow" and then in your binding do like:
<local:WeightConverter x:Key="weightConverter" RequiredUnit="{Binding DataContext.VmProp, ElementName=MainWindow}" />
Another way would be to do it using Relative Source binding.
Put x:Name="leapold" to your Window/Usercontrol
and make your binding with x:reference
<local:WeightConverter x:Key="weightConverter" RequiredUnit="{Binding DataContext.VmProp, Source={x:Reference leapold}}"/>
I have an Combobox with SelectedItem. If I select an item my setter does some calculation and perhaps I want to reset the value to the old one. Unfortunatly my view does no refresh.
I have the following ComboBox :
<ComboBox BorderThickness="0" VerticalAlignment="Center" Margin="2,0"
DisplayMemberPath="Name"
ItemsSource="{Binding ItemsVS.View}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}" >
</ComboBox>
Here are the properties of my ViewModel:
private CollectionViewSource _itemsVS;
public CollectionViewSource ItemsVS
{
get { return _itemsVS; }
set
{
_itemsVS = value;
if(PropertyChanged!=null)
PropertyChanged(this, new PropertyChangedEventArgs("ItemsVS"));
}
}
private ItemViewModel _selectedItem;
public ItemViewModel SelectedItem
{
get
{
// after setting the old value in the setter the getter is not called
// and the view is not refreshed
return _selectedItem;
}
set
{
var old = _selectedItem;
_selectedItem = value;
// After some logic I want to have the old value!!!!!!!!!!!!!!
_selectedItem = old;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
}
}
If you want to make the ComboBox read back the current value after setting the new value, you need to add a "no op" Converter to your Binding that effectively does nothing. It's a useful little trick, as a binding will not normally check to see whether the source value actually applied matches the new value provided by the binding. Adding a converter forces it to check.
public sealed class NoOpConverter : IValueConverter
{
public static readonly NoOpConverter Instance = new NoOpConverter();
public object Convert(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
return value;
}
public object ConvertBack(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
return value;
}
}
<ComboBox SelectedItem="{Binding Path=SelectedItem,
Mode=TwoWay,
Converter={x:Static NoOpConverter.Instance}}"
... />
I am finding it surprisingly hard to find examples of binding the visibility of a RibbonContextualTabGroup. I have a property in my code-behind that should decide when to display a ribbon tab, but everything I've tried so far has no effect. My code-behind is essentially:
public partial class MainWindow : RibbonWindow
{
public string Port { get; set; }
}
A summary of my WPF code is below. I'm looking for a solution that binds the Visibility property to whether or not MainWindow.Port is null.
<ribbon:RibbonWindow
...
xmlns:src="clr-namespace:MagExplorer" />
...
<ribbon:RibbonTab x:Name="COMTab"
Header="COM"
ContextualTabGroupHeader="Communications">
...
</ribbon:RibbonTab>
<ribbon:Ribbon.ContextualTabGroups>
<ribbon:RibbonContextualTabGroup Header="Communications"
Visibility="<What goes here?>" />
</ribbon:Ribbon.ContextualTabGroups>
You can create a Converter IsNotNullToVisibilityConverter
with the Convert method like this:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string)
{
if (!string.IsNullOrEmpty((string)value))
return Visibility.Visible;
}
else if (value != null)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
And then put it in your XAML
<Window.Resources>
<IsNotNullToVisibilityConverter x:Key="IsNotNullToVisibilityConverter" />
</Window.Resources>
...
Visibility="{Binding Path=Port, Converter={StaticResource IsNotNullToVisibilityConverter}}"
In your code behind:
public static readonly DependencyProperty PortProperty =
DependencyProperty.Register
("Port", typeof(String), typeof(NameOfYourClass),
new PropertyMetadata(String.Empty));
public String Port
{
get { return (String)GetValue(PortProperty); }
set { SetValue(PortProperty, value); }
}
Trying to get radiobuttons binding working but getting a run time error with the code below. Want the radio buttons to act such that only one can be selected at a time, and that they bind correctly in a 2 way fashion. Error text is.
"The invocation of the constructor on
type 'testapp1.MainWindow' that
matches the specified binding
constraints threw an exception"
<Window x:Class="testapp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:testapp1" Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<l:EnumBooleanConverter x:Key="enumBooleanConverter" />
</Grid.Resources>
<StackPanel >
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=FirstSelection}">first selection</RadioButton>
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=TheOtherSelection}">the other selection</RadioButton>
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=YetAnotherOne}">yet another one</RadioButton>
<Label Content="{Binding Path=VeryLovelyEnum}" Height="28" Name="label1" />
</StackPanel>
</Grid>
</Window>
And code:
namespace testapp1
{
public partial class MainWindow : Window
{
public TestModel _model;
public MainWindow()
{
InitializeComponent();
InitializeComponent();
_model = new TestModel();
this.DataContext = _model;
}
}
public enum MyLovelyEnum
{
FirstSelection,
TheOtherSelection,
YetAnotherOne
};
public class TestModel : DependencyObject
{
public MyLovelyEnum VeryLovelyEnum
{
get { return (MyLovelyEnum)GetValue(VeryLovelyEnumProperty); }
set { SetValue(VeryLovelyEnumProperty, value); }
}
public static readonly DependencyProperty VeryLovelyEnumProperty =
DependencyProperty.Register("VeryLovelyEnum", typeof(MyLovelyEnum), typeof(TestModel), new UIPropertyMetadata(0));
// from http://stackoverflow.com/questions/397556/wpf-how-to-bind-radiobuttons-to-an-enum
public class EnumBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object parameterValue = Enum.Parse(value.GetType(), parameterString);
return parameterValue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
return Enum.Parse(targetType, parameterString);
}
#endregion
}
}
The button & label is there from a previous test I did - I just left in...
The following declaration uses an integer instead of a default enum-value. Could be your instantiation problem...
public static readonly DependencyProperty VeryLovelyEnumProperty =
DependencyProperty.Register("VeryLovelyEnum", typeof(MyLovelyEnum), typeof(TestModel), new UIPropertyMetadata(0));
Try something like...
public static readonly DependencyProperty VeryLovelyEnumProperty =
DependencyProperty.Register("VeryLovelyEnum", typeof(MyLovelyEnum), typeof(TestModel), new UIPropertyMetadata(MyLovelyEnum.FirstSelection));