DependencyProperty binding not happening on initial load - silverlight

I'm trying to do something simple -- make a DependencyProperty and then bind to it. However, the getter doesn't appear to fire when I start up the app. (I'm guessing the solution will make me smack my head and go "Doh!", but still. :) )
Any ideas?
Code-behind code:
public static readonly DependencyProperty PriorityProperty =
DependencyProperty.Register("Priority",
typeof (Priority), typeof (PriorityControl), null);
public Priority Priority
{
get { return (Priority)GetValue(PriorityProperty); }
set { SetValue(PriorityProperty, value); }
}
Control XAML:
<ListBox Background="Transparent"
BorderThickness="0"
ItemsSource="{Binding Path=Priorities}"
Name="PriorityList"
SelectedItem="{Binding Path=Priority, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="16" Width="16">
<Border BorderBrush="Black"
BorderThickness="2"
CornerRadius="3"
Visibility="{Binding RelativeSource=
{RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}, Path=IsSelected, Converter={StaticResource boolToVisibilityConverter}}" />
<Border CornerRadius="3" Height="12" Width="12">
<Border.Background>
<SolidColorBrush Color="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}, Path=Content, Converter={StaticResource priorityToColorConverter}}" />
</Border.Background>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
Binding statement:
<UI:PriorityControl Grid.Column="8"
Priority="{Binding Path=Priority}"
VerticalAlignment="Center"/>
Some other notes:
Binding is in a UserControl
UserControl contains the PriorityControl
PriorityControl contains the DependencyProperty
I've checked that the data the UserControl is getting the appropriate data -- every other binding works.
If I change the selection on the PriorityControl via the mouse, everything fires as appropriate. It's just that initial setting of the value that isn't working.
Priority is an enum.
EDIT: I've tried two additional things. First, I made the binding to the value two-way, but that didn't work. Then, I added a property changed callback to the dependency property and made it call OnPropertyChanged, like so:
public static readonly DependencyProperty PriorityProperty =
DependencyProperty.Register("Priority", typeof (Priority), typeof (PriorityControl), new PropertyMetadata(HandleValueChanged));
private static void HandleValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var npc = dependencyObject as PriorityControl;
npc.OnPropertyChanged("Priority");
}
That didn't work either. I even tried overriding ApplyTemplate on the control to force a property change notification, but the value is never getting set on initial load. I can change values and see everything fire just fine, but it's that first time that nothing is coming through.

The getter and setter for a DependencyProperty are not called by anyone except you!
They are simply helpers generated for your benefit (to save you lots of casting and calls to GetValue/SetValue).
All work in DPs (animation, binding etc) happens directly via calls to SetValue and GetValue.
If you need to catch a change in value, provide the change callback in the Register call.
Note: Attached properties (i.e. using RegisterAttached instead of Register) actually do use the setter, while parsing the XAML, but that is another story as normal DPs do not.
Update:
You have not set a default value in your Register, so it defaults to 0 (which is your enum value "Normal"). If you set that as the first value in your binding there is not change so it won't trigger.
One Solution: Add a "none" enum as your first 0 value, so that "Normal" (value 1) will cause a change. Alternatively set a default value in the Register that is not "Normal".

I've noticed that when doing dependency properties if the value is already been set during the definition of the property it won't act correctly. You have said that there were no problems when setting the value within the application, so I'm going to assume there is no plumbing problems.
Consider the example below:
XAML
<UI:PriorityControl Priority="{Binding Path=Priority, Mode=TwoWay}"/>
Code
public enum Priority
{
Normal,
Low,
High
}
public class PriorityControl : UserControl
{
public static readonly DependencyProperty PriorityProperty =
DependencyProperty.Register("Priority", typeof(Priority), typeof(PriorityControl), new PropertyMetadata(DependencyProperty.UnsetValue, OnPriorityPropertyChanged);
public Priority Priority
{
get { return (Priority)this.GetValue(PriorityProperty); }
set { this.SetValue(PriorityProperty, value); }
}
private static void OnPriorityPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
PriorityControl control = sender as PriorityControl;
if (control != null)
{
// Do what you wanted to here.
}
}
}
If you're using an enum, and not specifying a default value (which you appear not to be by not providing a PropertyMetadata object to the dependency property on creation) the default value would be that of the enum's default value. In my above example that would be "Normal". Now if you tried to set the value to Normal on creating the control, the callback would not be called because the value already equals that value.
In order to allow this to work correctly, you must pass the DependencyProperty.UnsetValue to the PropertyMetadata object as I have done when I defined the property. That will allow the OnPriorityPropertyChanged method to be called when the value is being set initially.
Hope that helps fix it!

Not 100% sure, but it might be as simple as changing the Priority binding to be TwoWay.
<UI:PriorityControl Grid.Column="8"
Priority="{Binding Path=Priority, Mode=TwoWay}"
VerticalAlignment="Center"/>

Related

Button Visibility Binding doesn't update

I have a button on my Xaml page that its visibility is binded to a variable of Type : SYSTEM.Windows.Visibility in the view model, for the first time the page is loaded whether I set the variable to Hidden or Visible , it works fine. but aftre doing some operation when I change the variable to other status my GUI doesn't update.
Here are my xaml and MVVM
<Button Content="Extend" Name="btnExtend" Command="{Binding ExtendCommand}" Visibility="{Binding isVisible}" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Right" Width="80" Margin="0,0,100,0" Height="25"/>
and view model :
Public Property isVisible As System.Windows.Visibility
Public Sub New()
isVisible = System.Windows.Visibility.Visible
End Sub
Public Sub diable()
isVisible = System.Windows.Visibility.Visible
End Sub
I read in some topics to change the variable to Boolean and use a BooleanToVisibilityConverter , I tried this too, but the result was the same.
I really don't get it what I do wrong.
your xaml code should be
<Button Content="Extend" Name="btnExtend" Command="{Binding ExtendCommand}" Visibility="{Binding isVisible,Mode=Twoway}" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Right" Width="80" Margin="0,0,100,0" Height="25"/>
and your Viewmodel Code Should be
private Visibility _isVisible ;
public Visibility isVisible
{
get { return _isVisible ;}
set { _isVisible = value;RaisePropertyChanged("isVisible ");}
}
Check those links out:
http://msdn.microsoft.com/en-us/library/ms743695.aspx
http://www.daedtech.com/wpf-and-notifying-property-change
Tell your view that a property was changed by implementing INotifyPropertyChanged interface.
You need to fire a OnPropertyChanged() event in the setter of the Public Property to notify your changes to UI. Viewing your code, I seems that you are missing the OnPorpertyChanged() event implementation.
An another approach can be boolean values. Instead of make Visibility property, create a Boolean property and write a converter named "BooleanToVisibility" which will convert the value of "Visibility" Dependency Property.
Define Converter as a Static Resource in ResourceDictionary and then you can use it throughout the application. Converter approach is good to avoid rewriting show/hide logic in each view model.
Please let me know if you need working example on Converters.

WPF Radio Button Fires Converter in strange order

I am writing a simple program using the MVVM Model on WPF. Basicly when the user clicks a radio button in a group of radio buttons, it will update a property in the View Model with the new Account number. The problem is, when I click a different button the converter is called for the new button IsChecked Binding, and then after that it runs the converter for the previous button IsChecked binding(for losing its checked status).
This is causing a problem, since the new button is updating the value of the property with the correct account number, and then when the old button calls the converter, it gets converted back to the old value. I have hacked it to work by adding a static variable to the class, and if the IsChecked property is false, just return the value in the static variable. Does anyone have a better solution for Short Circuting the Converter Call on the box that loses its checked status. Code is below:
Converter:
class RadioToAccountConverter : IValueConverter
{
static string myValue; //HACK TO MAKE IT WORK
object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return parameter.ToString();
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((bool)value)
{
myValue = parameter.ToString(); // Hack to make it work
return parameter.ToString();
}
return myValue; // Hack to make it work
}
}
XAML:
<RadioButton Foreground="HotPink"
Grid.Column="0"
Content="6087721"
Tag="6087721"
IsChecked="{Binding Account, Converter={StaticResource Radio2Value}, Mode=OneWayToSource, ConverterParameter=6087721}">
</RadioButton>
<RadioButton Foreground="HotPink"
Grid.Column="1"
Content="BFSC120"
IsChecked="{Binding Account, Converter={StaticResource Radio2Value}, Mode=OneWayToSource, ConverterParameter='BFSC120'}">
</RadioButton>
<RadioButton Foreground="HotPink"
Grid.Column="2"
Content="BFSC121"
IsChecked="{Binding Account, Converter={StaticResource Radio2Value}, Mode=OneWayToSource, ConverterParameter=BFSC121}">
</RadioButton>
<RadioButton Foreground="HotPink"
Grid.Column="3"
Content="BFSC206"
IsChecked="{Binding Account, Converter={StaticResource Radio2Value}, Mode=OneWayToSource, ConverterParameter=BFSC206}">
</RadioButton>
Property:
public const string AccountPropertyName = "Account";
private string _account;
/// <summary>
/// Sets and gets the Account property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string Account
{
get
{
return _account;
}
set
{
if (_account == value)
{
return;
}
RaisePropertyChanging(AccountPropertyName);
_account = value;
RaisePropertyChanged(AccountPropertyName);
}
}
Any Help Is Greatly Appreciated.
Based on what I understand, you want to give users the ability to select from a list of account numbers. You're choice of presentation (view) is a group of radio buttons.
If that is true, the key part is this: you want to give users the ability to select from a list of account numbers. This means that the control you should use is a ListBox, since users should select one of the appropriate values. Now, since you are looking to use radio buttons visually, you simply have to supply an alternative ItemsSource.ItemContainerStyle.
XAML:
<ListBox ItemsSource="{Binding AccountNumbers, Mode=OneWay">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton Content="{Binding}" IsChecked="{Binding IsSelected, RelativeSource={x:Static RelativeSource.TemplatedParent}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Note that you'll need to add another property on your ViewModel (I named it AccountNumbers). For example:
public IReadOnlyCollection<string> AccountNumbers { ... }
Of course, the underlying collection can be a observable if you need it to be, but that's really up to you.
If you define a GroupName on each RadioButton, WPF will manage the IsChecked states for you.
You could bind the state with a {Binding SomeProperty, Mode=OneWayToSource} if you want the ViewModel to be aware of state.
One way to approach this would be to bind each RadioButton's IsChecked property to the whole ViewModel, just bind it to something like
IsChecked="{Binding WholeViewModel, Mode=OneWayToSource, Converter={StaticResource MyRadioButtonConverter}, ConverterParameter=SomethingReallyUnique}"
...where the public property WholeViewModel is a property that does a return this; in the getter. This would let you have access to the ViewModel and enough information to query the ViewModel to see if the radiobutton should be checked or not. But, only do this if the GroupName DependencyProperty doesn't somehow give you what you want.
To process the clicking on the buttons, then, to actually change the ViewModel state, you'd implement an ICommand in your ViewModel and bind the Command property of the RadioButton to {Binding ClickedCommand} and define a CommandParameter with any string you want. This approach will guarantee a one-way relationship to the IsChecked state, preventing the thing you're describing, I think.
I'll work up a code sample if you think you'd like one.

Bind user control dependency properties in MVVM style Windows Phone app

I'm having some issues with binding some custom controls in a Windows Phone app right now. Usually this is never an issue but apparently my mind can't comprehend this today.
So I'm doing an MVVM style setup which is good. I have my page with a view and also a viewmodel. Now on a WebClient callback I assign the dataContext of my view to the list of models in my ViewModel, nice and simple thus far...now in my view I created a ListBox with a custom control in the datatemplate which is basically a cell in the list. I once again set my user controls dataContext to binding, and binding all the models values to the regular UI elements works no problem.
Here's a sample:
<Grid Grid.Column="0">
<Image Source="{Binding SmallPath}" VerticalAlignment="Top"/>
</Grid>
<Grid Grid.Column="1">
<StackPanel Margin="12,0,0,0">
<TextBlock x:Name="MemberId_TextBlock" Text="{Binding MemberId}" FontSize="28"
Margin="0,-8,0,0"
Foreground="{StaticResource PhoneForegroundBrush}"/>
<StackPanel Orientation="Horizontal" Margin="0,-11,0,0">
<TextBlock Text="{Binding DaysReported}" FontSize="42"
Margin="0,0,0,0"
Foreground="{StaticResource PhoneAccentBrush}"/>
<TextBlock Text="days" FontSize="24"
Margin="3,19,0,0"
Foreground="{StaticResource PhoneSubtleBrush}"/>
</StackPanel>
</StackPanel>
</Grid>
That's in my user control, and here's the the view where the usercontrol is housed:
<Grid x:Name="LayoutRoot" Background="Transparent">
<ListBox Name="TopSpotter_ListBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<!--<TextBlock Text="{Binding MemberId}"/>-->
<controls:TopSpotterItemControl DataContext="{Binding}"/>
<Grid Height="18"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Now this is good enough but what I want to do in my view is set data from my model like Booleans that determine whether or not I should show certain Grids etc. So if I try to set a dependency property explicitly in my control it fires and will run logic in the Getter/Setters for instance. HOWEVER if I try to set these custom objects from a binding source it won't actually set.
Here's what works:
<controls:TopSpotterItemControl ChampVisibility="True">
This way will trigger the ChampVisibility property and then in the code behind of the user control I can set visibilities.
Here's what fails but I want to work:
<controls:TopSpotterItemControl ChampVisibility="{Binding IsChamp">
In addition I can still set the DataContext to {Binding} and the result will be unchanged.
In this scenario IsChamp is part of my model that I would like to bind to this user control which I guess comes from the dataContext being set on the view from the viewModel. I'm not sure what I can do to get this so the bindings work etc. without having to set custom properties.
Finally, here's my user control:
public partial class TopSpotterItemControl : UserControl
{
public string MemberId
{
get
{
return this.MemberId_TextBlock.Text;
}
set
{
this.MemberId_TextBlock.Text = value;
}
}
public bool ChampVisibility {
set
{
if (value)
{
this.Champ_Grid.Visibility = System.Windows.Visibility.Visible;
}
}
}
public static readonly DependencyProperty MemberNameProperty =
DependencyProperty.Register("MemberId", typeof(string), typeof(TopSpotterItemControl), new PropertyMetadata(null));
public static readonly DependencyProperty ChampVisibilityProperty =
DependencyProperty.Register("ChampVisibility", typeof(bool), typeof(TopSpotterItemControl), new PropertyMetadata(null));
public TopSpotterItemControl()
{
InitializeComponent();
}
}
Bit long winded and I hope I made things on the issue clear. My one major hang up so far, and I'd like to abstract as much control as I can to the user control via dependency properties explicitly set in xaml, rather than setting up binding in its xaml that depend on the knowledge of a model. Thanks!
Your DependencyProperty is badly formed. (I also don't see Champ_Grid defined in your class or XAML, but I assume that is an ommission)
Setting ChampVisibility = true in code works because it is unrelated to the DependencyProperty.
You can tell easily because the default value for your DP is invalid. It will compile, but the instance constructor will through an exception if it is ever invoked.
new PropertyMetadata(null)
bool = null = exception
If you call GetValue(TopSpotterItemControl.ChampVisibilityProperty) from somewhere you can confirm all of the above.
You should make changes to instance fields in the property changed handler and declare the property like the following, it will work:
Note that the property has to change (not just be set) for the event to be raised.
public bool ChampVisibility
{
get { return (bool)GetValue(ChampVisibilityProperty); }
set { SetValue(ChampVisibilityProperty, value); }
}
public static readonly DependencyProperty ChampVisibilityProperty =
DependencyProperty.Register("ChampVisibility ", typeof(bool), typeof(TopSpotterItemControl), new PropertyMetadata(true, (s, e) =>
{
TopSpotterItemControl instance = s as TopSpotterItemControl;
instance.Champ_Grid.Visibility = instance.ChampVisibility ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}));
Incidentally, your MemberId DependencyProperty is also completely wrong and cannot work.
Note:
The Binding on your TextBox works, because it is binding to the DataContext (your model), so it probably shows the right value.
The Dependency property in your UserControl will never be set though.
Use the propdp code-snippet in Visual Studio so you dont have to concern yourself with the complexities of Dependency Property declaration.
Also check this out for more info about Dependency Properties

WPF databinding with a user control

I have a wpf user control, which exposes a single custom dependency property. Inside the user control, a textblock binds to the value of the dp. This databinding works in all scenarios except when the data source is an object.
The minimal code necessary to reproduce this is:
this is the main part of the user control
<StackPanel Orientation="Horizontal">
<TextBlock Text="**SimpleUC** UCValue: "/>
<TextBlock Text="{Binding UCValue}"/>
</StackPanel>
and the user control code behind:
public SimpleUC()
{
InitializeComponent();
this.DataContext = this;
}
public string UCValue
{
get { return (string)GetValue(UCValueProperty); }
set { SetValue(UCValueProperty, value); }
}
public static readonly DependencyProperty UCValueProperty =
DependencyProperty.Register("UCValue", typeof(string), typeof(SimpleUC), new UIPropertyMetadata("value not set"));
this is the test window. I imported my project xml namespace as "custom"
<Window.Resources>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel>
<TextBlock Text="This fails to bind:"/>
<custom:SimpleUC UCValue="{Binding SomeData}"/>
</StackPanel>
<StackPanel>
<TextBlock>The same binding on a regular control like Label</TextBlock>
<Label Content="{Binding SomeData}"/>
</StackPanel>
<Slider x:Name="sld" />
<StackPanel>
<TextBlock>However, binding the UC to another element value, like a slider works</TextBlock>
<custom:SimpleUC UCValue="{Binding ElementName=sld,Path=Value}"/>
</StackPanel>
</StackPanel>
and the test window code behind is:
public TestWindow()
{
InitializeComponent();
this.DataContext = this;
}
//property to bind to
public string SomeData { get { return "Hello S.O."; } }
When I turn on the diagnostic tracing on the TestWindow, it spits out the error "BindingExpression path error:
'SomeData' property not found on 'object' ''SimpleUC' (Name='')' ... "
The binding expression is the same as the one I used in the neighboring label and it worked fine. This behavior seems really bizarre to me. Can anyone shed some light?
You set DataContext of your SimpleUC to itself here
public SimpleUC()
{
InitializeComponent();
this.DataContext = this; // wrong way!
}
so when you use binding here
<custom:SimpleUC UCValue="{Binding SomeData}"/>
it searches property SomeData in control's data context which is set to this object because code in SimpleUC constructor overrides value of DataContext and it is not set to TestWindow object anymore as you expected. That's why your solution works - it doesn't affect DataContext which is inherited from window. Also you can keep this.DataContext = this; but set element where to search property explicitly like this (skipped irrelevant)
<Window ... Name="wnd1">
<custom:SimpleUC UCValue="{Binding SomeData, ElementName=wnd1}"/>
...
But my oppinion is that your variant from the answer looks more convenient to me, setting data context to this is not very good practice.
Hope it helps.
If you must use a UserControl, your
<TextBlock
Text="{Binding RelativeSource={RelativeSource Self},
Path=Parent.Parent.UCValue}"
/>
is an ok way to do it and
<TextBlock
Text="{Binding UCValue,
RelativeSource={RelativeSource FindAncestor,custom:SimpleUC,1}}"
/>
is better because you don't rely on the control hierarchy and possible instantiation order issues.
However I would recommend for this kind of situation that you use "custom controls" instead of "user controls". They take a little bit of getting used to, but they are much more powerful because their XAML is the template itself which means you can use TemplateBinding and {RelativeSource TemplatedParent}.
In any case, DataContext = this; is definitely to be avoided.

Binding property to Silverlight dependency property independent of DataContext

I'm trying to make an Address control that has an IsReadOnly property, which will make every TextBox inside read only when set to true.
<my:AddressControl Grid.Column="1" Margin="5" IsReadOnly="True"/>
I've managed to do this just fine with a dependency property and it works.
Here's a simple class with the dependency property declared :
public partial class AddressControl : UserControl
{
public AddressControl()
{
InitializeComponent();
this.DataContext = this;
}
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool),
typeof(AddressControl), null);
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
}
In the XAML for this codebehind file I have a Textbox for each address line:
<TextBox IsReadOnly="{Binding IsReadOnly}" Text="{Binding City, Mode=TwoWay}"/>
<TextBox IsReadOnly="{Binding IsReadOnly}" Text="{Binding State, Mode=TwoWay}"/>
<TextBox IsReadOnly="{Binding IsReadOnly}" Text="{Binding Zip, Mode=TwoWay}"/>
Like i said this works just fine.
The problem is that the Address control itself is bound to its parent object (I have several addresses I am binding).
<my:AddressControl DataContext="{Binding ShippingAddress, Mode=TwoWay}" IsReadOnly="True">
<my:AddressControl DataContext="{Binding BillingAddress, Mode=TwoWay}" IsReadOnly="True">
The problem is that as soon as I set DataContext to something other than 'this' then the binding for IsReadOnly breaks. Not surprising because its looking for IsReadOnly on the Address data entity and it doesn't exist or belong there.
I've tried just about every combination of binding attributes to get IsReadOnly to bind to the AddressControl obejct but can't get it working.
I've tried things like this, but I can't get IsReadOnly to bind independently to the AddressControl property instead of its DataContext.
<TextBox IsReadOnly="{Binding RelativeSource={RelativeSource Self}, Path=IsReadOnlyProperty}" Text="{Binding City, Mode=TwoWay}" />
I think I'm pretty close. What am I doing wrong?
With this answer (actually my own answer to a similar question) I have a good [better] solution.
I still have to iterate through the textboxes, but I don't have to set the actual value. I can create bindings in the codebehind - just not with XAML.
I think you're stuck, at least, if you want to do this just via binding. My guess is that you're going to have to resort to code-behind, presumably by iterating through your child textbox controls and setting their IsReadOnly propert as a side-effect of your Address control's IsReadOnly property.
Unlike some folks who think that any code sitting in a code-behind file is effectively an admission of failure, I don't get religious about it: if throwing some code into a code-behind is the easiest way to do something, that's where I put my code. On the contrary, if I have to spend half a day trying to figure out how to do something via binding that I could do in five minutes with a code-behind, that's failure, IMO.

Resources