In my user control I have this property:
public static DependencyProperty FooListProperty = DependencyProperty.Register(
"FooList", typeof(List<Problem>), typeof(ProblemView));
public List<Problem> FooList
{
get
{
return (List<Problem>)GetValue(FooListProperty);
}
set
{
SetValue(FooListProperty, value);
}
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == FooListProperty)
{
// Do something
}
}
And since another window, I´m trying to set a value for the last user control:
<local:ProblemView HorizontalAlignment="Center"
VerticalAlignment="Center" FooList="{Binding list}" />
And that window in load contains:
public List<Problem> list;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Some processes and it sets to list field
list = a;
}
But in XAML code, binding it isn't working. Don't pass the data. What am I wrong?
You can't bind to a Field in WPF, you'll have to change list to a property instead.
You call the Dependency Property FooList in your UserControl and ResultList in Xaml but I'm guessing that's a typo in the question.
You should implement INotifyPropertyChanged in the Window to let the Bindings know that the value has been updated.
I'm not sure if you have the correct DataContext set in the Xaml ProblemView, if you're unsure you can name the Window and use ElementName in the binding
<Window Name="window"
...>
<!--...-->
<local:ProblemView HorizontalAlignment="Center"
VerticalAlignment="Center"
ResultList="{Binding ElementName=window,
Path=List}" />
<!--...-->
</Window>
Sample code behind
public partial class MainWindow : Window, INotifyPropertyChanged
{
//...
private List<Problem> m_list;
public List<Problem> List
{
get { return m_list; }
set
{
m_list = value;
OnPropertyChanged("List");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Related
I'm struggeling a bit with a bindable property and the propertyChanged event not firing when new text is entered.
I've made a minimal codesample:
Xaml custom control:
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BindingPropertyProject.CustomFlyout">
<Entry x:Name="MyEntry"/>
Codebehind:
public partial class CustomFlyout : Grid
{
public CustomFlyout()
{
InitializeComponent();
}
public string MyEntryText
{
get { return (string)GetValue(MyEntryTextProperty); }
set
{
SetValue(MyEntryTextProperty, value);
}
}
public static readonly BindableProperty MyEntryTextProperty =
BindableProperty.Create(nameof(MyEntryText), typeof(string),
typeof(CustomFlyout),
defaultValue: string.Empty,
defaultBindingMode: BindingMode.TwoWay
, propertyChanging: TextChanged);
private static void TextChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is CustomFlyout control)
{
control.MyEntry.Text = newValue?.ToString();
}
}
}
}
Consuming class xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BindingPropertyProject"
x:Class="BindingPropertyProject.MainPage">
<Grid>
<local:CustomFlyout MyEntryText="{Binding TextPropertyFromBindingContext, Mode=TwoWay}" HorizontalOptions="FillAndExpand" VerticalOptions="Start"/>
</Grid>
Consuming class codebehind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
private string _textPropertyFromBindingContext = "bound";
public string TextPropertyFromBindingContext
{
get
{
return _textPropertyFromBindingContext;
}
set
{
if (_textPropertyFromBindingContext != value)
{
_textPropertyFromBindingContext = value;
OnPropertyChanged();
}
}
}
}
It binds the "bound" value just fine, but subsequent changes entered in the entry does not raise property changed.
I've tried a number of suggestions i found from googeling, but this should be fine right?
UPDATE:
Ok - so i actually got i to work by adding binding in the custom view:
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BindingPropertyProject.CustomFlyout">
<Entry x:Name="MyEntry" Text="{Binding TextPropertyFromBindingContext }"/>
Is this really the way to do it? I mean - i could only make it work, if bindings was named EXACTLY the same in custom view, and consuming part..
i could only make it work, if bindings was named EXACTLY the same in
custom view, and consuming part..
It's not necessary to have same binding name. Please refer following code.
Custom Control
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="StackQA2XF.CustomControl.MyCustomControl">
<ContentView.Content>
<Entry x:Name="CustomEntry"/>
</ContentView.Content>
</ContentView>
public partial class MyCustomControl : ContentView
{
public static readonly BindableProperty EntryTextProperty =
BindableProperty.Create(nameof(EntryText), typeof(string), typeof(MyCustomControl), default(string), BindingMode.TwoWay);
public string EntryText
{
get { return (string)GetValue(EntryTextProperty); }
set { SetValue(EntryTextProperty, value); }
}
public MyCustomControl()
{
InitializeComponent();
CustomEntry.SetBinding(Entry.TextProperty, new Binding(nameof(EntryText), source: this));
}
}
Consuming Class
<customcontrols:MyCustomControl EntryText="{Binding TitleText}"/>
public class MainViewModel : INotifyPropertyChanged
{
private string _titleText = "Good morning";
public string TitleText
{
get
{
return _titleText;
}
set
{
_titleText = value;
OnPropertyChange(nameof(TitleText));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChange(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Please do binding in the code for custom control and raise property change for the binding property in viewmodel.
CustomEntry.SetBinding(Entry.TextProperty, new Binding(nameof(EntryText), source: this));
OnPropertyChange(nameof(TitleText));
Please refer https://www.youtube.com/watch?v=ZViJyL9Ptqg.
I have tested this code able to get fired propertyChanged event when Entry text is changed from custom view.
It binds the "bound" value just fine, but subsequent changes entered in the entry does not raise property changed.
From Bindable Properties property changes, BindableProperty MyEntryTextProperty binding TextPropertyFromBindingContext, so the propertyChanged event will be fired when you change TextPropertyFromBindingContext, Instead of changing the value of MyEntry.
You can change TextPropertyFromBindingContext bu Button.click, then you will see the propertyChanged event will be fired.
public partial class Page3 : ContentPage, INotifyPropertyChanged
{
private string _textPropertyFromBindingContext = "bound";
public string TextPropertyFromBindingContext
{
get
{
return _textPropertyFromBindingContext;
}
set
{
if (_textPropertyFromBindingContext != value)
{
_textPropertyFromBindingContext = value;
RaisePropertyChanged("TextPropertyFromBindingContext");
}
}
}
public Page3()
{
InitializeComponent();
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private void btn1_Clicked(object sender, EventArgs e)
{
TextPropertyFromBindingContext = "test";
}
}
I am using the mahapps metro app to make a gui in wpf. I have used the code
<Controls:Badged Badge="{Binding Path=BadgeValue}">
<!-- Control to wrap goes here -->
<Button Content="Notifications" />
</Controls:Badged>
Say, if i want to update the 'BadgeValue' in the notifications callback, how do i go about doing this? plz help..
You set the BadgeValue source property that you bind to in your XAML and raise the PropertyChanged event, just like you would update any other data-bound property.
Here is an example for you:
View Model:
public class ViewModel : INotifyPropertyChanged
{
private string _badgeValue;
public string BadgeValue
{
get { return _badgeValue; }
set { _badgeValue = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
viewModel.BadgeValue = "new value...";
}
}
MainWindow.xaml:
<Controls:Badged Badge="{Binding Path=BadgeValue}">
<Button Content="Notifications" />
</Controls:Badged>
<Button Content="Update" Click="Button_Click" />
I need to set a property in the Business Logic with a method in the Business Logic. If you run my code you can see the first String "Target Location" changes successfully, but the second one "Some Other String" doesn't change its value in the view. "PropertyChanged" in the BusinessLogic.cs is null. I have absolutely no idea WHY it's null! Can someone explain me this behaviour and how I can fix this?
I have the following files in my project:
MainWindow.xaml
<Window x:Class="TestWpf.MainWindow"
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:TestWpf"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBox Text="{Binding Path=TargetLocation}"></TextBox>
<TextBox Text="{Binding Path=SomeOtherString}"></TextBox>
<Button Click="ChangeTextButton_Click">Change Target Location</Button>
<Button Click="ChangeSomeOtherStringButton_Click">Change some other string</Button>
</StackPanel>
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
MainViewModel mainViewModel = new MainViewModel();
mainViewModel.TargetLocation = #"A:\Old_Location";
mainViewModel.SomeOtherString = "Old String...";
DataContext = mainViewModel;
}
private void ChangeTextButton_Click(object sender, RoutedEventArgs e)
{
MainViewModel mainViewModel = (MainViewModel)DataContext;
mainViewModel.TargetLocation = #"B:\New_Location";
}
private void ChangeSomeOtherStringButton_Click(object sender, RoutedEventArgs e)
{
MainViewModel mainViewModel = (MainViewModel)DataContext;
mainViewModel.ChangeSomeOtherString();
}
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
private string targetLocation;
public string TargetLocation
{
get
{
return targetLocation;
}
set
{
targetLocation = value;
OnPropertyChanged("TargetLocation");
}
}
public string SomeOtherString
{
get
{
return BusinessLogicClass.GetInstance().SomeOtherString;
}
set
{
BusinessLogicClass.GetInstance().SomeOtherString = value;
OnPropertyChanged("SomeOtherString");
}
}
public void ChangeSomeOtherString()
{
BusinessLogicClass.GetInstance().ChangeSomeOtherString();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
BusinessLogicClass
public class BusinessLogicClass : INotifyPropertyChanged
{
private static BusinessLogicClass instance;
public static BusinessLogicClass GetInstance()
{
if (instance == null)
{
instance = new BusinessLogicClass();
}
return instance;
}
private BusinessLogicClass()
{
}
private string someOtherString;
public string SomeOtherString
{
get
{
return someOtherString;
}
set
{
someOtherString = value;
OnPropertyChanged("SomeOtherString");
}
}
public void ChangeSomeOtherString()
{
SomeOtherString = "New String!";
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
"PropertyChanged" in the BusinessLogic.cs is null. I have absolutely no idea WHY it's null!
PropertyChanged in the BusinessLogic class is null because there are no bindings that use properties in this class as their source. The source properties for both of your bindings are on your MainViewModel class.
WPF doesn't scan through all classes that happen to implement INotifyPropertyChanged. And even if it did, how would it know that a PropertyChanged event fired from your BusinessLogic class means that it needs to update the TextBox bound to the SomeOtherString property on your MainViewModel? WPF can't read your code to find this out.
The simplest fix is to fire a PropertyChanged event inside your ChangeSomeOtherString() method:
public void ChangeSomeOtherString()
{
BusinessLogicClass.GetInstance().ChangeSomeOtherString();
OnPropertyChanged("SomeOtherString"); // Add this line
}
This way WPF knows that the value of the SomeOtherString property has changed and will perform the necessary update to the TextBox.
I have created blank C#/XAML Windows 8 application. Add simple XAML code:
<Page
x:Class="Blank.MainPage"
IsTabStop="false"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
Margin="0,150"
HorizontalAlignment="Center">
<TextBlock
x:Name="xTitle"
Text="{Binding Title, Mode=TwoWay}"/>
<Button Content="Click me!" Click="OnClick" />
</StackPanel>
</Grid>
</Page>
And the simple code in C# part:
public sealed partial class MainPage
{
private readonly ViewModel m_viewModel;
public MainPage()
{
InitializeComponent();
m_viewModel = new ViewModel
{
Title = "Test1"
};
DataContext = m_viewModel;
}
private void OnClick(object sender, RoutedEventArgs e)
{
m_viewModel.Title = "Test2";
}
}
Now I want to implement ViewModel. I have two way:
Use Dependency Property
Implement INotifyPropertyChanged
For first approach it is:
public class ViewModel : DependencyObject
{
public string Title
{
get
{
return (string)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
}
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string)
, typeof(ViewModel)
, new PropertyMetadata(string.Empty));
}
For second it is:
public class ViewModel : INotifyPropertyChanged
{
private string m_title;
public string Title
{
get
{
return m_title;
}
set
{
m_title = value;
OnPropertyChanged("Title");
}
}
protected void OnPropertyChanged(string name)
{
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I prefer the first way, because it allows use coerce (Silverlight for web and for WP7 doesn't have coerce functionality.. WinRT too.. but I'm still looking and hope) and looks more natural for me. But unfortunately, it works as OneTime for the first approach.
Could anybody explain to me why MS abandon using Dependency Property for implementing view model?
You should not be using a DependencyProperty in your ViewModel - you should only use them in your controls. You will never want to bind one ViewModel to another, also ViewModels do not need to persist their values nor provide default values, nor provide property metadata.
You should only use INotifyPropertyChanged in your ViewModels.
I cannot get any display from my observable collection in a custom object bound to a ListBox. This works fine when I have a string collection in my view model, but no names display when I try to access the property through a custom object. I am not receiving any errors in the output window.
Here is my code:
Custom Object
public class TestObject
{
public ObservableCollection<string> List { get; set; }
public static TestObject GetList()
{
string[] list = new string[] { "Bob", "Bill" };
return new TestObject
{
List = new ObservableCollection<string>(list)
};
}
}
Xaml
<Window x:Class="TestWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Height="100" HorizontalAlignment="Left" Margin="120,61,0,0" Name="listBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Path=TObj.List}" />
</Grid>
Xaml.cs
public partial class MainWindow : Window
{
private ModelMainWindow model;
public MainWindow()
{
InitializeComponent();
model = new ModelMainWindow();
this.DataContext = model;
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
public void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.model.Refresh();
}
}
ViewModel
public class ModelMainWindow : INotifyPropertyChanged
{
private TestObject tObj;
public event PropertyChangedEventHandler PropertyChanged;
public TestObject TObj
{
get
{
return this.tObj;
}
set
{
this.tObj = value;
this.Notify("Names");
}
}
public void Notify(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public void Refresh()
{
this.TObj = TestObject.GetList();
}
}
Can't bind to private properties. Also the change notification targets the wrong property, change "Names" to "TObj". (Also i would recommend making the List property get-only (backed by a readonly field), or implementing INoptifyPropertyChanged so the changes cannot get lost)
Your List is private. Make it a public property otherwise WPF can't see it.