I'm working on my first usercontrol ang got stuck on following problem
Changing the Value of binded property doesn’t even call the property getter (a textbox does)
What is the reason for this behavior?
<luc:IpTextBox x:Name="IpTxtBxIpv4Address"
Grid.Row="1" Grid.Column="1"
IsEnabled="{Binding VmNicData.EnableIpSetting}"
Mode="IPv4Address"/>
<TextBox x:Name="TxtBxTest"
Grid.Row="1" Grid.Column="2"
IsEnabled="{Binding VmNicData.EnableIpSetting}"
Text="{Binding VmNicData.IpV4SubnetMask}"/>
ViewModel:
private bool _ipEnabled;
public bool IpEnabled
{
get => _ipEnabled;
set
{
_ipEnabled = value;
OnPropertyChanged(nameof(IpEnabled));
OnPropertyChanged(nameof(EnableIpSetting));
}
}
private bool _dhcpEnabled;
public bool DhcpEnabled
{
get => _dhcpEnabled;
set
{
_dhcpEnabled = value;
OnPropertyChanged(nameof(DhcpEnabled));
OnPropertyChanged(nameof(EnableIpSetting));
}
}
public bool EnableIpSetting => _ipEnabled && !_dhcpEnabled;
Thank's in advance
What I've done so far:
My user control contains just some textboxes. I’ve got that DataContext = this; right after initializing my usercontroll.
Setting the IsEnabled static to true or false has the expected effect on these texboxes.
As soon as I try to bind it to the property it doesn’t work at all (even if Property is false at start texboxes aren’t disabled).
I set a breakpoint at the property which doesn’t hit.
I added this txtBxTest. To see if Binding works there. It does, breackpoint was hit.
Solved: My mistake was to change the datacontext (DataContext = this in the constructor of my usercontrol). That broke my binding to parent.
Anyway thank you all for supporting
You're binded to the "EnableIpSetting", but you are not calling OnPropertyChanged when it is updated.
You need to add your OnPropertyChanged logic to the EnableIpSetting as well.
Related
googling for this showed me that this is often a problem but never reallay solved.
I do have an App/Prgramm in C#, i'll try to be mvvm conform.
I do have an window, in it a UserControl show different views.
One of my view contains a textbox, the text of the textbox is bound to a proptery of the VM.
My textbox got 2 inputbindings, for "enter" and "return" - both leading to same command.
On hitting "enter" the value of the textbox should be processed, the textbox shoud be cleared and refocused ... This works .... One Time ....
Clearing the textbox with String.Empty breaks the Bindings ... this could be found in several postings here ... the most Solution is textboxname.clear() ...
But i dont have the "textboxname" in the viewmodel, only in code-behind, but all my logic is in the VM ... So can somebody pls help me sort things out, how i could clear the textbox without breaking the bindings to input text and hit enter more then one time ?
My Code
<TextBox x:Name="tb_checkinbox" Grid.Row="0" Grid.Column="1" Width="200" Height="25" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding CheckInNumber}">
<TextBox.InputBindings>
<KeyBinding Command="{Binding OnCheckInEnterCommand}" Key="Return"/>
<KeyBinding Command="{Binding OnCheckInEnterCommand}" Key="Enter"/>
</TextBox.InputBindings>
</TextBox>
public CheckinVM()
{
OnCheckInEnterCommand = new RelayCommand(OnCheckInEnter, CanCheckInEnter);
}
private string _checkInNumber;
public string CheckInNumber
{
get { return _checkInNumber; }
set { SetProperty(ref _checkInNumber, value); }
}
public ICommand OnCheckInEnterCommand { get; set; }
public void OnCheckInEnter(object value)
{
CheckInNumber = String.Empty;
/// More to do
}
public bool CanCheckInEnter(object value)
{
return true;
}
The assignment
CheckInNumber = string.Empty;
does not "clear" any Binding. Your conclusion is wrong.
You do however only get empty strings - after clearing - in the setter of the CheckInNumber property. In order to get property updates not only when the TextBox loses focus or you reset the source property, set UpdateSourceTrigger=PropertyChanged on the Text Binding:
<TextBox ... Text="{Binding CheckInNumber, UpdateSourceTrigger=PropertyChanged}">
I have a weird problem with something simple I suppose.
I have a combobox with two bindings set up - one for ItemsSource and another for SelectedItem.
The selected item is not working on initial startup, but then it works OK. Output does not indicate any binding problems, I have also set up a TextBlock with the same binding to see if it works - and it does.
Here's the code
<ComboBox IsSynchronizedWithCurrentItem="True" IsEditable="False"
Name="ProgramsCollectionComboBox"
SelectedItem="{Binding ElementName=ThisUc,
Path=SelectedProgram}"
ItemsSource="{Binding ElementName=ThisUc,
Path=ProgramsCollection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="{Binding ElementName=ThisUc,
Path=SelectedProgram.Name, Mode=TwoWay}" />
The property:
private Program _selectedProgram;
public Program SelectedProgram
{
get
{
if (_selectedProgram == null)
{
_selectedProgram = new Program(Settings.Default.SelectedProgramPath);
}
return _selectedProgram;
}
set
{
_selectedProgram = value;
Settings.Default.SelectedProgramPath = SelectedProgram.PathProgramFolder;
RaisePropertyChanged("SelectedProgram");
}
}
It saves and reads the settings OK, the initial values is shown in the textblock below the combobox, when I change the selected item, the textblock is updated, the settings are changed and everything works fine - except for the fact that on app startup, selected item is not selected.
Thanks for help!
There are two reasons your initial binding is not working. First, as Jehof has mentioned himself, is the fact that you're setting your SelectedProgram to an item that is not part of the ProgramsCollection.
Furthermore, when you are setting the initial value of your SelectedProgram you are doing so in the getter, where PropertyChanged is not invoked and thus the binding will never be aware of that change. You can either invoke PropertyChanged when initializing it in the getter:
...
get
{
if (_selectedProgram == null)
{
_selectedProgram = _programsCollection?.FirstOrDefault();
RaisePropertyChanged("SelectedProgram");
}
return _selectedProgram;
}
...
Or even better, set the default value on the private field:
private Program _selectedProgram = _programsCollection?.FirstOrDefault();
...
The getter of your property SelectedProgram should return a value of your ProgrammsCollection and not a new instance if it is null.
If the value is not part of the collection that is bound to the combobox it is not displayed.
I have a textblock that is a 'status label'. I want this label to be updated, and when that happens, I want its color to be also set automatically - as well as visibility (Label invisible until it has content).
The problem is, that if I specify anything more than the text binding, then the textblock does not change (i.e. text does not appear, and it is still hidden).
Actually, I tried also without binding visibility, and it appears that the Foreground also blocks the binding.
<TextBlock x:Name="StatusInfo"
HorizontalAlignment="Center" VerticalAlignment="Bottom"
FontSize="9"
Visibility="{Binding ElementName=ThisUc,
Path=StatusLabelVisibility}"
Text="{Binding ElementName=ThisUc,
Path=StatusLabel}"
Foreground="{Binding ElementName=ThisUc,
Path=StatusLabelBrush}" />
This is all in a UserControl, so I am using dependency properties for StatusLabel property, as I want to bind it to properties in main window... Foreground and Visibility properties are not dependency properties, as I don't want to expose them.
This is my property setter and getter:
public string StatusLabel
{
get { return (string)GetValue(StatusLabelProperty); }
set
{
SetValue(StatusLabelProperty, value);
RaisePropertyChanged("StatusLabel");
if (value != string.Empty)
{
StatusLabelVisibility = System.Windows.Visibility.Visible;
if (value.HasAny("success", "ok") && !value.HasAny("partial"))
{
StatusLabelBrush = Brushes.Green;
}
else if (value.HasAny("fail"))
{
StatusLabelBrush = Brushes.DarkRed;
}
else if (value.HasAny("partial"))
{
StatusLabelBrush = Brushes.DarkGoldenrod;
}
else
{
StatusLabelBrush = Brushes.Black;
}
}
else
{
StatusLabelVisibility = System.Windows.Visibility.Collapsed;
}
}
}
Please let me know what am I doing wrong, perhaps that's not the way to go altogether?
Cheers
====================
While Meredith's answer solved the issue, let me just post a comment for future reference (as it was not obvious for me):
Here it goes - if you assign the UserControl property directly, not via property binding, it appears to lose the 'bound' - and if you try to change the bound property again, it won't update the control as it would have before it 'lost the bound'
Cheers
If StatusLabel is a DependencyProperty, you can't put anything else in the setter - it won't get called correctly. Look up the way to do changed events for DependencyProperties instead. You need a PropertyChangedCallback. Check out How to use PropertyChangedCallBack. Raise your prop changes, and set all the other properties in the callback.
In my UserControl I have a Checkbox
<CheckBox DockPanel.Dock="Left" VerticalAlignment="Bottom" VerticalContentAlignment="Bottom" x:Name="showLegendsChk" Margin="10,0,0,0"
Content="View Legends" Checked="showLegendsChk_Checked" />
<!--IsChecked="{Binding ElementName=CrossSecViewWnd, Path=ShowLegends, Mode=TwoWay}" -->
I tried to add data binding to it, & added some logic on checked & non-checked; so no need to add event to the same.
private bool showLegendsWnd;
public CrossSectionalViewControl() {
FillLegends();
ShowLegends = false;
}
// Using a DependencyProperty as the backing store for
//IsCheckBoxChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowLegendsProperty =
DependencyProperty.Register("ShowLegends", typeof(bool),
typeof(CrossSectionalViewControl), new UIPropertyMetadata(false));
public bool ShowLegends
{
get { return showLegendsWnd; }
set
{
showLegendsWnd = value;
NotifyPropertyChanged("ShowLegends");
if (showLegendsWnd == true)
legendWrap.Visibility = System.Windows.Visibility.Visible;
else
legendWrap.Visibility = System.Windows.Visibility.Hidden;
Console.WriteLine("Show Legends = " + showLegendsWnd + " Chk Value = " + showLegendsChk.IsChecked);
}
}
Tried alot wit binding, but no success. Finally added checked event & commented binding property. -
private void showLegendsChk_Checked(object sender, RoutedEventArgs e)
{
showLegendsWnd = (bool)showLegendsChk.IsChecked;
Console.WriteLine("CHK Show Legends = " + showLegendsWnd + " Chk Value = " + showLegendsChk.IsChecked);
if (showLegendsWnd == true)
legendWrap.Visibility = System.Windows.Visibility.Visible;
else
legendWrap.Visibility = System.Windows.Visibility.Hidden;
legendWrap.UpdateLayout();
}
With this also, even when checkbox is unChecked, it doesn't fire event nor property with both checked & unchecked.
In both - Binding & Event 1 state event is fired properly but the other isn't !!! Have also added TwoWay mode, tried with UpdateSourceTrigger in binding but no success.
Why is this Strange problem with Checkbox....
For your event:
You also need to subscribe to the Unchecked event.
Change your xaml to:
<CheckBox x:Name="showLegendsChk"
DockPanel.Dock="Left"
VerticalAlignment="Bottom"
VerticalContentAlignment="Bottom"
Margin="10,0,0,0"
Content="View Legends"
Checked="showLegendsChk_Checked"
Unchecked="showLegendsChk_Checked" />
Now both events will fire the same handler and if you set a breakpoint in your handler, you can see it get called.
For your binding
Not quite sure what you're trying with it. Firstly the property you define for your DP's are merely a convenience for you and the underlying framework does not invoke it's setter's or getter's when updating the DP value. Next up, not sure why you're calling a NotifyPropertyChanged("ShowLegends"); on a DP. If my assumtion is right and that actually corresponds to a INPC raise property changed implementation, it's not needed for DP's
Start with trying simple stuff. Something like:
<CheckBox x:Name="chkBox"
IsChecked="{Binding IsChecked}" />
and in your DataContext class have the corresponding property
private bool _isChecked;
public bool IsChecked {
get { return _isChecked; }
set {
_isChecked = value;
Debug.WriteLine(string.Format("Checkbox check state changed to: {0}", _isChecked ? "Checked" : "Not Checked"));
}
}
You should see the Debug.WriteLine get invoked on changes to the IsChecked property. once you got to that stage add the rest of your logic step by step and validate if it still works and if it doesn't, it's prolly something you're adding causing it than a behavior of the system.
Update:
Download Link to sample
Attached sample should show three approaches. Event based, Simple Binding, Complex Binding connected to a DP from a custom control to then toggle that control's visibility.
I have text box,i binded its Text property to MainTxtBoxTxt property in ModalView of Window.When MainTxtBoxTxt changes it affects to TextBox,but when Text property of TextBox changes it doesnt affect to MainTxtBoxTxt in ModelView.What is the problem?
<cstmTxtBox:CustomTextBox Grid.Row="0" TextWrapping="Wrap" FontSize="16" x:Name="TxtBox" cstmTxtBox:CustomTextBox.CaretIndex="{Binding Path=CaretIndex, Mode=TwoWay}" Text="{Binding Path=MainTxtBoxText,Mode=TwoWay}" >
CustomTxtBox
public class CustomTextBox : TextBox
{
public CustomTextBox()
{
}
public static DependencyProperty CaretIndexProperty =DependencyProperty.RegisterAttached( "CaretIndex",typeof(int),typeof(CustomTextBox),new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsRender));
public static void SetCaretIndex(UIElement element, int value)
{
element.SetValue(CaretIndexProperty, value);
}
public static int GetCaretIndex(UIElement element)
{
return (int)element.GetValue(CaretIndexProperty);
}
}
Try this
Text="{Binding Path=MainTxtBoxText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Use following:
<cstmTxtBox:CustomTextBox Grid.Row="0" TextWrapping="Wrap" FontSize="16" x:Name="TxtBox" cstmTxtBox:CustomTextBox.CaretIndex="{Binding Path=CaretIndex, Mode=TwoWay}" Text="{Binding Path=MainTxtBoxText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
By default text box update view model on focus lost. Specifying UpdateSourceTrigger=PropertyChanged will update view model every time text in text box changes.
You were missing UpdateSourceTrigger property in your code which specifies how the change to data should reflect in the model property. To elaborate, UpdateSoruceTrigger property has four options to select from :-
Default - which returns the default UpdateSourceTrigger value of the target dependency property. It varies with control.
LostFocus - Changes reflect when focus move away from the current control.
PropertyChanged - reflect changes as soon as data changes. For a textbox, whenever a key is pressed, the changes occur.
Explicit - As the name suggests, it occurs on your command. you must call the UpdateSource method or the changes will not propagate back to the source