Why does TextBlock "T1" not show "101" after clicking on Button "B1" and still shows "100"?
<StackPanel>
<TextBlock Name="T1" Text="{x:Bind value, Mode=OneWay}"/>
<Button Name="B1" Content="+1" Click="B1_Click"/>
</StackPanel>
and
public sealed partial class MainPage : Page
{
public int value;
public MainPage()
{
InitializeComponent();
value = 100; // initial value
}
private void B1_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
value = value + 1;
}
}
Your value is a field, yes it works with x:Bind. Actually it should even work being private.
But in order for the UI to update the value of value, you need to make one of the three changes below -
Call Bindings.Update() after setting it.
change it to a dependency property.
Change it to a normal property but implement INotifyPropertyChanged on your page and raise property changed event on the setter. You can read more from here.
But which one to pick? This is normally what I do -
If the property rarely changes, I use Bindings.Update() and remove Mode=OneWay from the binding to have the best performance.
If the property lives in the code-behind of a UI element (like in your case), I go with a dependency property.
If the property lives inside a ViewModel, I use INPC.
Related
I have a user control and im trying to bind one of its properties
User Control Xaml
<UserControl x:Class="pi_browser.Testing.Example"
...
x: Name="LabelControl">
<StackPanel x:Name="RootStackPanel">
<Label Content="{Binding Text, ElementName=LabelControl}"/>
</StackPanel>
</UserControl>
User Control Codebehind
public partial class Example : UserControl
{
public Example()
{
InitializeComponent();
ExampleViewModel vm = new ExampleViewModel(State);
DataContext = vm;
}
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State",
typeof(Boolean), typeof(Example), new PropertyMetadata(false));
}
Main Page View Model
class ExampleViewModel
{
public ExampleViewModel(bool v)
{
val = v;
}
bool val;
public string Text { get => val ? "This worked" : "This didnt work"; }
}
Main Window Xaml
<Window x:Class="pi_browser.Testing.Tester" ... >
<Grid>
<local:Example State="True"/>
</Grid>
</Window>
In this example I didn't bind the State variable, I only passed a literal, but ideally I would like to bind to actual values.
State is a boolean, yet you bind to Text. Let us fix one issue by creating a dependency property Text on your User Control. We shall fix the Text issue and not the boolean State issue. Once you fix that, do the same for State.
So to fix Text we need to fix why this fails:
<Label Content="{Binding Text, ElementName=LabelControl}"/>
You set the ElementName to be the UserControl itself, which is what one wants. But then you tell the binding to look for (remember binding is just reflection of an object under the covers) the property Text. The property Text does not exist on that instance/class...but State does. Its obvious to bind to a newly created Text dependency property on the user control to fix the first issue.
Then when you instantiate the control on your main page, you need to then, and only then bind to Text because that property also resides on your viewmodel.
So three things, along with the change mentioned on the UserControl:
Make your ViewModel adhere to INotifyPropertyChanged and make the Text property use the notification mechanism you install.
Make sure that your main page has its DataContext set to a vailid instance of your ViewModel class.
Bind to Text such as <local:Example State="{Binding Text}"/>
Once that is done, the Text value will properly flow towards the UserControl.
Well, not the problem, just working on my course work and trying an MVVM pattern. All is working well or I think so. But this thing shouldn't work and I don't understand why is it okay...
Here is a ViewModel:
public class ViewModel : OnPropertyChangedClass
{
private readonly static IService service = new Service();
private static reFolder selectedFolder;
private ObservableCollection<reObject> folders;
public ObservableCollection<reObject> Folders
{
get => folders;
set
{
folders = value;
OnPropertyChanged();
}
}
private reFolder folderExplorer;
public reFolder FolderExplorer
{
get => folderExplorer;
set
{
folderExplorer = value;
OnPropertyChanged();
}
}
//these methods are called from MainWindow.xaml.cs, it's the only way I've found to track nodes
public static void NodeExpanded()
{
service.UpdateFolder(selectedFolder);
service.ChangeFolderExpandedState(selectedFolder);
}
public static void NodeSelected(reFolder folder)
{
selectedFolder = folder;
service.UpdateExplorer(selectedFolder);
}
public ViewModel()
{
folders = service.UpdateDrives();
folderExplorer = service.UpdateExplorer(null);
}
}
And the part in MainWindow.xaml
<DataGrid x:Name="Explorer" ItemsSource="{Binding FolderExplorer.FoldersFiles}" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" FontWeight="Bold" FontSize="16" IsReadOnly="True" Width="168" Foreground="#FF1BB37C"/>
</DataGrid.Columns>
</DataGrid>
As I understand, DataGrid updates after calling OnPropertyChanged(), but it's only called once in the constructor:
folderExplorer = service.UpdateExplorer(null);
But when I just select something in the TreeView in runtime, xaml.cs calles NodeSelected method:
service.UpdateExplorer(selectedFolder);
It not triggers setter or OnPropertyChanged in debug, but DataGrid still updates.
Can somebody explain me this, please. If you need more code, just tell me, thanks.
Your question is a bit difficult to understand. I read it like you wonder about the different behavior of the DataGrid compared to the TreeView?
Your expectations are correct: the DataGrid will update the cell, if the bound property has changed.
But this is never happening in your case - at least this is what I can tell from your posted code. Opposed to your claims, even your posted constructor is not raising the PropertyChanged event:
// Assignment of a field. Fields never raise PropertyChanged (your code)
folderExplorer = service.UpdateExplorer(null);
// Assignment of a property. Properties can raise PropertyChanged
FolderExplorer = service.UpdateExplorer(null);
You didn't posted this code, because you thought it is unimportant. That's why I assume you are expecting the execution of the FolderExplorer set method.
But this is not happening for a good reason: the properties that are actually edited and updated are inside the FolderExplorer.FoldersFiles collection. Set a breakpoint into the class of the items that are contained in the FoldersFiles on the Name property and you will see, that the properties set methods are called whenever you edit the corresponding DataGrid cell.
If a nested property changes e.g. FolderExplorer.FoldersFiles/FolderFile.Name, then PropertyChanged is only raised on the actual (nested) property and not on all the top level properties along the path of a nested property call.
Furthermore your comparison of the DataGrid behavior with the TreeView behavior is not valid. Editing a cell modifies the data while selecting doesn't. So clicking on a cell won't raise any PropertyChanged events in the first place. In this scenario a property change would only occur, if you bind the DataGrid.SelectedItem to a property of your ViewModel.
I have a custom UserControl. It is composed of a TextBox and a Button. The objective of this user control is to allow the user to see the ToString() content of an object, which is set in the XAML via Binding and also allow them to set the object to NULL.
This is the XAML and C# code of the UserControl
<UserControl
<Grid>
<TextBox Text="{x:Bind SelectedObject.ToString(), Mode=OneWay}" IsReadOnly="True" />
<Button Click="buttonDeselectObject_Click" IsEnabled="True" Content="X"/>
</Grid>
</UserControl>
public sealed partial class ObjectSelectorBox : UserControl
{
public ObjectSelectorBox ()
{
this.InitializeComponent ();
textBoxSelectedObject.AddHandler (TappedEvent, new TappedEventHandler (TextBox_Tapped), true);
}
public object SelectedObject
{
get => GetValue (SelectedObjectProperty);
set => SetValue (SelectedObjectProperty, value);
}
public static readonly DependencyProperty SelectedObjectProperty = DependencyProperty.Register ("SelectedObject", typeof (object), typeof (ObjectSelectorBox), null);
private void buttonDeselectObject_Click (object sender, RoutedEventArgs e)
{
//This is where the SelectedObject binding source should be set to NULL
SelectedObject = null; //This works but does NOT reflect to the binding source
SelectedObject = "123"; //This works and does reflect to the binding source
}
}
This is how I use it in a UWP Page:
<local1:ObjectSelectorBox SelectedObject="{x:Bind TestObject, Mode=TwoWay}"/>
When the user presses the button the SelectedObject value should be set null. While this does work, the change does not reflect to the binding source which is TestObject (string).
However when I set SelectedObject value to something else like "123" or "abc" the change is correctly reflected to TestObject.
Is this behaviour intentional and is there a way to change it so changed to SelectedObject always reflect to the binding source?
Binding source not updating when DependencyProperty's value is set to null
Iuri Farenzena's comment is correct, it fails when call ToString() with null object. Why we can't get exception within Visual Studio. Because the exception has captured internally and stop reflecting to the binding source.
For your requirement, please use string.Empty to replace null.
SelectedObject = string.Empty
Update
Op's solution
Implementing a custom IValueConverter and use value?.ToString() to determine if the value is null.
Data templates are great, but I'm having a problem with binding in a particular situation. I have a class, Value, that has various descendants like StringValue, DateValue, etc. These Values show up in a Listbox. This template works fine, binding to a specific property of StringValue:
<DataTemplate DataType="{x:Type values:StringValue}">
<TextBox Margin="0.5"
Text="{Binding Path=Native}" />
</DataTemplate>
However, when I bind to an object itself, instead of a specific property, the changes don't update the object, as in this template:
<DataTemplate DataType="{x:Type values:LookupValue}">
<qp:IncrementalLookupBox SelectedValue="{Binding Path=., Mode=TwoWay}"
LookupProvider="{Binding ElementName=EditWindow, Path=ViewModel.LookupProvider}">
</qp:IncrementalLookupBox>
</DataTemplate>
IncrementalLookupBox is a UserControl that ultimately allows a user to select a LookupValue, which should replace the item bound in the template. If this was bound to a simple type like an int or string, the binding would replace the object, so I'm not sure what the difference is with a more complex object. I know that the IncrementalLookBox is working, because binding some textboxes to the properties of SelectedValue (which is a dependency property) shows the correctly selected LookupValue.
In case it makes the situation more clear, here is the implementation of SelectedValue:
public LookupValue SelectedValue
{
get { return (LookupValue)GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(LookupValue), typeof(IncrementalLookupBox), new PropertyMetadata(OnSelectedValuePropertyChanged));
private static void OnSelectedValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = d as IncrementalLookupBox;
obj.OnSelectedValuePropertyChanged(e);
}
private void OnSelectedValuePropertyChanged(DependencyPropertyChangedEventArgs e)
{
CheckForSelectedValueInLookups();
}
If all else fails consider using a ValueConverter to get the value you require.
Edit: this does not work. See link in comments below.
Make sure your class implements INotifyPropertyChanged and raise PropertyChanaged here:
private void OnSelectedValuePropertyChanged(DependencyPropertyChangedEventArgs e)
{
CheckForSelectedValueInLookups();
// RaisePropertyChanged();
}
My issue is the same as described here:
WPF TwoWay Binding of ListBox using DataTemplate
Apparently if I don't write enough text here, my answer will be converted to a comment and not close out the question. So, to summarize the issue, a two-way Binding=. in a datatemplate used in a ListBox (or any ItemsControl I image) won't work, because it is not the object itself being bound, but the ListBoxItem that contains it.
I have a dependency property defined as below. It is defined in xaml.cs of Childusercontrol. It always uses the default value of RGB(255,0,0) ie. Red.
public Color ForeColor
{
get {return (Color)this.GetValue(ForeColorProperty); }
set {this.SetValue(ForeColorProperty, value);}
}
public static readonly DependencyProperty ForeColorProperty = DependencyProperty.Register("ForeColor", typeof(Color), typeof(Childusercontrol), new PropertyMetadata(Color.FromRgb(255,0,0), OnCurrentForeColorPropertyChanged));
private static void OnCurrentForeColorPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Childusecontrol control = source as Childusecontrol;
Color fcolor= (Color)e.NewValue;
}
The value is passed through xaml from parent usercontrol as
<UC:Childusercontrol ForeColor="{Binding ChildForeColor}"/>
ChildForeColor is a property of type Color in ViewModel of ParentUserControl and is defined as below.
private Color _ChildForeColor;
public Color ChildForeColor
{
get
{
return _ChildForeColor ;
}
set
{
if (_ChildForeColor != value)
{
_ChildForeColor = value;
RaisePropertyChanged(()=> ChildForeColor );
}
}
}
And ChildForeColor property is set as below, in the parentusercontrol's constructor.
The value being passed as constructor parameter is blue.
public Parentusercontrol(System.Drawing.Color ForeColor)
{
ChildForeColor = Color.FromRgb(ForeColor.R, ForeColor.B, ForeColor.G);
}
But, the InitializeComponent(); of Parent control's xaml.cs clears the value of dependency property and hence, only the default value is used.
Do I have to change the definition of the dependency property? How to fix this bug?
This worked perfectly fine for me!
ChildControl
I gave the UserControl a Name in Xaml i.e
<UserControl ... (all normal namespaces)... x:Name="Child">
<Border>
<Border.Background>
<SolidColorBrush Color="{Binding ForeColor, ElementName=child}"/>
</Border.Background>
</Border>
</UserControl>
The property "ForeColor" is a dependency property as you defined it yourself. This control works perfectly on its own too.
ParentControl
I did the same as with ChildControl. i.e. gave it a name.
<UserControl ... (Usual NS)... x:Name="parent">
<Border BorderThickness="2">
<local:ChildUserControl Margin="5" ForeColor="{Binding ChildForeColor, ElementName=parent}"/>
<Border.BorderBrush>
<SolidColorBrush Color="{Binding ChildForeColor, ElementName=parent}"/>
</Border.BorderBrush>
</Border>
</UserControl>
This also works fine with testing the C# Class looks as follows
public ParentUserControl(System.Drawing.Color c)
{
InitializeComponent();
Color c2 = Color.FromRgb(c.R, c.G, c.B);
ChildForeColor = c2;
}
private Color _ChildForeColor = Color.FromRgb(0, 255, 0);
public Color ChildForeColor
{
get { return _ChildForeColor; }
set
{
if (value != _ChildForeColor)
{
_ChildForeColor = value;
OnPropertyChanged(() => ChildForeColor);
}
}
}
I have assigned the _ChildForeColor a value just for testing, but this is not needed. Please note however that if you run a NotifyPropertyChanged event this cannot happen before InitializeComponent(); This I guess is because nothing yet has been initialized to listen to the change. Therefore you have 2 options. Remove OnPropertyChanged and assign color before InitializeComponent, or use OnPropertyChanged but only assign color after InitializeComponent. The first solution will still work because the property value is changed before the components go and look for the value.
Window for using constructing the controls
This is a bit more tricky as you have assigned a constructor that takes a variable. So my code looks as follows:
public Control ParContent
{
get { return (ContentControl)GetValue(ParContentProperty); }
set { SetValue(ParContentProperty, value); }
}
//Register Dependency ParContent Property
public static readonly DependencyProperty ParContentProperty = DependencyProperty.Register("ParContent", typeof(ContentControl), typeof(MainWindow), new PropertyMetadata( ));
public MainWindow()
{
InitializeComponent();
ParContent = new ParentUserControl(System.Drawing.Color.Blue);
}
and in Xaml
<Window ...Title="MainWindow" Height="478.784" Width="736.87" x:Name="win">
<Grid>
<local:ChildUserControl HorizontalAlignment="Left" Height="100" Margin="122,298,0,0" VerticalAlignment="Top" Width="100"/>
<ContentControl x:Name="Parent" Content="{Binding ParContent,ElementName=win}" HorizontalAlignment="Left" Margin="106,49,0,0" VerticalAlignment="Top" Height="79" Width="93"/>
</Grid>
</Window>
As I said this worked perfectly fine by me and all the properties keep their values.
Possible solutions:
Make sure the parent's childForeColor has a color assigned to it especially when using ordinary properties.
If you use ordinary properties in Parent control make sure INotifyPropertyChange is called if the color is changed after Initialize (Which I guess you subscribe to already)
perhaps use FrameworkPropertyMetadata instead and then add flag AffectsRender - don't think this is the problem, but worth a shot
Play around with the Binding Mode - although I do not think this is the real issue either
If you are working with 2 x controls where 1 property is most likely going to inherit from another use Inherited properties rather - http://msdn.microsoft.com/en-us/library/ms753197(v=vs.110).aspx
Bottom line I have a suspicion that the Parent's "ChildForeColor" might be causing the problem as the above seems ok to me at first glance.
EDIT
Try doing the following. In xaml give your parent control a name x:name="Parent" then in the binding mode do this
<UC:Childusercontrol ForeColor="{Binding ChildForeColor, ElementName="Parent"}"/>
This should sort out any binding issues if the problem lies with the binding.
However you say "Parent control's xaml.cs clears the value of dependency property and hence, only the default value is used." Which indicates that the problem is not with binding or with the child control as far as I can gather...
I also assumed you have stepped through the code so after you hit this
ChildForeColor = Color.FromRgb(ForeColor.R, ForeColor.B, ForeColor.G);
ChildForeColor appears correct and then if you override OnInitialized() and evaluate the value of ChildForeColor after base.OnInitialized(e); has run the ForeColor is still unchanged?
With this I also assume you have not removed InitializeComponent(); from the constructor, and InitializeComponent(); comes after ChildForeColor = ....! In your constructor you do not show where InitializeComponent() is and I assumed it was just for easy reading purpose.
If ForeColor remained unchanged at this point and assuming base.OnInitialized is the first method that runs in OnInitialized. Then Initialization is not the problem, then the alternative suggestion is to change ChildForeColor to a proper dependency property:
public Color ChildForeColor
{
get { return (Color)GetValue(ChildForeColorProperty); }
set { SetValue(ChildForeColorProperty, value); }
}
//Register Dependency ChildForeColor Property
public static readonly DependencyProperty ChildForeColorProperty = DependencyProperty.Register("ChildForeColor", typeof(Color), typeof(ParentControl), new FrameworkPropertyMetadata());
and see if that changes it.