Automatic updte of ListView items - c#

I am new to WPF Binding. Is there any way the listview automatically update when one of the item in ItemSource modifies its own dependecny property. I was trying it to do with FreezableCollection.
My code is given below and the aim is to update the listbox when the textbox is modified.
MainWindow.xaml
<Grid x:Name="mainDataGrid">
<StackPanel Orientation="Horizontal">
<ListView x:Name="membersListView" ItemsSource="{Binding}" MinWidth="100"/>
<StackPanel>
<TextBox x:Name="selectedItemTextBox" Text="{Binding ElementName=membersListView, Path=SelectedItem.Name, Mode=TwoWay}" MinWidth="200"/>
</StackPanel>
</StackPanel>
</Grid>
MainWindow.cs
public partial class MainWindow : Window
{
ViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
vm.Add(new Model() { Name = "Name1" });
vm.Add(new Model() { Name = "Name2" });
this.DataContext = vm;
}
}
public class Model : Freezable
{
public String Name
{
get { return (String)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public override string ToString()
{
return Name;
}
// Using a DependencyProperty as the backing store for Name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(String), typeof(Model), new PropertyMetadata(""));
protected override Freezable CreateInstanceCore()
{
return new Model();
}
}
public class ViewModel : FreezableCollection<Model>
{
}

Ok,
Right now your ListView is showing the String Representation of your models, That's why you had to override the "ToString()" method... because you couldn't get it to understand to show the Name property.
Now what happens is that your TextBox changes the Name property well but your listbox doesn't know that "Name" property has changed... because it's looking at ToString()
if you set the "DisplayMemberPath" of your ListView to "Name" , it will not look at ToString(), but rather "Name"... like this:
<ListView x:Name="membersListView" ItemsSource="{Binding}" DisplayMemberPath="Name" MinWidth="100"/>
Note that in this mode if you change the Name property using textbox, the textbox won't update the value of "Name" Property instantly until it loses focus, so to fix that change the binding of textbox text to this:
<TextBox x:Name="selectedItemTextBox" Text="{Binding ElementName=membersListView, Path=SelectedItem.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="200"/>
I've added "UpdateSourceTrigger=PropertyChanged" to ensure that as you start changing the text of TextBox, the Name property is updated instantly.
:) hope it helps.

Related

Binding Items in Tab Control ItemSource to a User Controls DependencyProperty [duplicate]

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
How to correctly bind to a dependency property of a usercontrol in a MVVM framework
(4 answers)
Closed 5 years ago.
I have a tab control that is created from a list of objects and a WPF User control that I want to associate directly with one of these objects. I am creating this association as an Dependency Property in the control.
The binding error I get is:
System.Windows.Data Error: 1 : Cannot create default converter to perform 'one-way' conversions between types 'WPFTester.Control' and 'WPFTester.TestClass'. Consider using Converter property of Binding. BindingExpression:Path=; DataItem='Control' (Name=''); target element is 'Control' (Name=''); target property is 'ClassDependency' (type 'TestClass')
It seems as though it changes its mind what the datacontext is before it binds.
The Main Window Creates the list of Objects and attaches them to a tab control. The Issue I am having regards the binding to the Control Dependency Object here.
XAML:
<Grid>
<TabControl ItemsSource="{Binding Classes}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<local:Control ClassDependency="{Binding}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
Code-Behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private BindingList<TestClass> classes;
public event PropertyChangedEventHandler PropertyChanged;
public BindingList<TestClass> Classes
{
get { return classes; }
set
{
classes = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Classes"));
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Classes = new BindingList<TestClass>
{
new TestClass() { Title = "123", Content="abc" },
new TestClass() { Title = "456", Content="def" }
};
}
The rest of the classes are simple and I don't think the issue is in them but I will provide them anyways.
The Data Class is simple:
public class TestClass
{
public string Title { get; set; }
public string Content { get; set; }
}
The User control displays a bound value and a constant value. It also presents a Dependency Property.
XAML:
<StackPanel>
<TextBlock Text="{Binding TextDependency}" Margin="5"/>
<TextBlock Text="I'm here"/>
</StackPanel>
Code-Behind:
public partial class Control : UserControl
{
public static readonly DependencyProperty ClassDependencyProperty =
DependencyProperty.RegisterAttached(
"ClassDependency",
typeof(TestClass),
typeof(Control));
public TestClass ClassDependency
{
get { return (TestClass)GetValue(ClassDependencyProperty); }
set { SetValue(ClassDependencyProperty, value); }
}
public Control()
{
InitializeComponent();
this.DataContext = this;
}
}

UserControl DataContext Binding

I have three projects in my solution:
My main WPF Application which contains a MainWindow + MainViewModel
UserControl Library with a UserControl (ConfigEditorView)
UIProcess class with the ViewModel for the UserControl (ConfigEditorViewModel)
In my MainWindow I want to use the UserControl with the ViewModel of UIProcess.
First I set the UserControl in my MainWindow:
<TabItem Header="Editor">
<Grid>
<cel:ConfigEditorView DataContext="{Binding ConfEditModel, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</TabItem>
I don't know which of these properties I need here, so I put all together but it still doesn't work.
Then I've set this in my MainViewModel:
public ConfigEditorViewModel ConfEditModel { get; set; }
With simple method that is bound to a Button:
private void doSomething()
{
ConfEditModel = new ConfigEditorViewModel("Hello World");
}
My ConfigEditorViewModel looks basically like this:
public class ConfigEditorViewModel : ViewModelBase
{
private string _Description;
public string Description
{
get
{
return _Description;
}
set
{
_Description = value;
base.RaisePropertyChanged();
}
}
public ConfigEditorViewModel(string t)
{
Description = t;
}
}
The description is bound to a TextBox in my UserControl.
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,10" Text="{Binding Description}"/>
When I start the application and click the Button the TextBox should contain "Hello World" but it's empty.
What I've done wrong?
i gave you a general answer:
within a "real(a usercontrol you wanna use with different viewmodels with different property names)" usercontrol you bind just to your own DependencyProperties and you do that with ElementName or RelativeSource binding and you should never set the DataContext within a UserControl.
<UserControl x:Name="myRealUC" x:class="MyUserControl">
<TextBox Text="{Binding ElementName=myRealUC, Path=MyOwnDPIDeclaredInMyUc, Path=TwoWay}"/>
<UserControl>
if you do that you can easily use this Usercontrol in any view like:
<myControls:MyUserControl MyOwnDPIDeclaredInMyUc="{Binding MyPropertyInMyViewmodel}"/>
and for completeness: the Dependency Property
public readonly static DependencyProperty MyOwnDPIDeclaredInMyUcProperty = DependencyProperty.Register(
"MyOwnDPIDeclaredInMyUc", typeof(string), typeof(MyUserControl), new PropertyMetadata(""));
public bool MyOwnDPIDeclaredInMyUc
{
get { return (string)GetValue(MyOwnDPIDeclaredInMyUcProperty); }
set { SetValue(MyOwnDPIDeclaredInMyUcProperty, value); }
}
Your view models (and, optionally, models) need to implement INotifyPropertyChanged.
Binding's aren't magic. There is no inbuilt mechanism that allows for code to be notified when a plain old property's value changes. You'd have to poll it in order to check to see if a change happened, which would be very bad, performance-wise.
So bindings will look at the objects they are bound against and see if they implement INotifyPropertyChanged and, if so, will subscribe to the PropertyChanged event. That way, when you change a property and fire the event, the binding is notified and updates the UI.
Be warned, you must implement the interface and use it correctly. This example says it's for 2010, but it works fine.

The databinding of a usercontrol doesn't update the source model

I'm trying to implement a custom textbox which has a placeholder text. The content of the 'FirstName' property of my model appears in the textbox, as intended. The problem I'm having is when I change the text of the textbox, it isn't updated back in the source model. Why is that?
I've tried setting the binding mode to "TwoWay", but it doesn't change anything. Is there something I'm doing wrong?
Edit: Silly me! As it turns out, I had to put Mode="TwoWay" on both bindings, not just the usercontrol's. I'll mark as answered as soon as possible.
Model.cs
public class Student
{
public string FirstName { get; set; }
}
MainWindow.xaml
<grid>
<ui:prettyTextbox Text="{Binding FirstName}" PlaceholderText="#Enter your name">
</grid>
PrettyTextbox.xaml
<UserControl x:Name="prettyTextbox">
<Grid>
<TextBlock Text="{Binding Path=PlaceholderText, ElementName=prettyTextbox}"
Visibility="{Binding Path=Text, ElementName=prettyTextbox, Converter={StaticResource StringLengthToVisibilityConverter}}"/>
<TextBox Text="{Binding Path=Text, ElementName=prettyTextbox, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
PrettyTextbox.xaml.cs
public partial class PrettyTextbox : INotifyPropertyChanged
{
public static readonly DependencyProperty PlaceholderTextProperty =
DependencyProperty.Register("PlaceholderText", typeof (string),
typeof(PrettyTextbox), new FrameworkPropertyMetadata(default(string)));
public string PlaceholderText
{
get { return (string)GetValue(PlaceholderTextProperty); }
set
{
SetValue(PlaceholderTextProperty, value);
OnPropertyChanged();
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string),
typeof(PrettyTextbox), new FrameworkPropertyMetadata(default(string)));
public string Text
{
get { return (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
OnPropertyChanged();
}
}
public PrettyTextbox()
{
InitializeComponent();
}
}
}
You forgot to make the text property bind two way by default, so you need to change this part:
<ui:prettyTextbox Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
or change FrameworkPropertyMetadata of the text property to:
new FrameworkPropertyMetadata
{
DefaultValue = null,
BindsTwoWayByDefault = true
}

Use a binding to set the text property of a textbox in a usercontrol - WPF

I have a user control which contains a textbox and have created a get/set in the usercontrol to get/set the text property of the textbox.
public class OpenFileControl : UserControl
{
StackPanel sp;
public TextBox tb;
public string Text { get { return tb.Text; } set { tb.Text = value; } }
I then want to set this value based on a binding later on -
<gX3UserControls:OpenFileControl Text="{Binding Value}" />
But I get the following exception
A 'Binding' cannot be set on the 'Text' property of type 'OpenFileControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
After some investigation It seems Text needs to be a dependency property, but If I do that I cant work out how to pass the value on to the textbox.
How can I fix this.
Consider using something like this.
Control XAML:
<UserControl x:Class="WpfTestBench.OpenFileControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=Filename, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</UserControl>
Control codebehind:
using System.Windows;
namespace WpfTestBench
{
public partial class OpenFileControl
{
public static readonly DependencyProperty FilenameProperty =
DependencyProperty.Register("Filename", typeof (string), typeof (OpenFileControl));
public OpenFileControl()
{
InitializeComponent();
}
public string Filename
{
get { return (string)GetValue(FilenameProperty); }
set { SetValue(FilenameProperty, value); }
}
}
}
Main XAML:
<Window x:Class="WpfTestBench.OpenFileWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfTestBench="clr-namespace:WpfTestBench"
Title="OpenFileWindow" Width="300" SizeToContent="Height">
<StackPanel>
<wpfTestBench:OpenFileControl x:Name="In" Filename="{Binding SelectedFilename, UpdateSourceTrigger=PropertyChanged}" />
<wpfTestBench:OpenFileControl x:Name="Out" Filename="{Binding ElementName=In, Path=Filename}" />
</StackPanel>
</Window>
Main codebehind:
namespace WpfTestBench
{
public partial class OpenFileWindow
{
public OpenFileWindow()
{
InitializeComponent();
DataContext = this;
}
public string SelectedFilename { get; set; }
}
}
Execution result (after typing something in the first control):
If you define the dependency property as the static and the actual property, you can write whatever code behind you want in the body of the property.
public const string TextPropertyName = "Text";
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
TextPropertyName,
typeof(string),
typeof(MyControl),
new UIPropertyMetadata(false));
In the getter and setter you can do something like textBox1.Text = value; but you'd probably be better served using a binding to the property instead. MVVM frameworks make light work of this sort of thing quite often. You might find more success defining a ViewModel (a class with an appropriate FielPath variable for example) and setting the DataContext of the new UserControl to be an instance of the ViewModel class, using Bindings to do the heavy lifting for you.

WPF databinding to an other class

I've created a WPF UI. The following code exists in MainWindow.xaml.cs:
namespace AWPFProject
{
public partial class MainWindow : Window
{
private readonly ServiceLogic serviceLogic;
public MainWindow()
{
InitializeComponent();
serviceLogic = new ServiceLogic ();
}
}
}
Servicelogic is my central class. From there, methods or classes are called to handle stuff like database management.
Now, that ServiceLogic class has the values I'd like to bind to.
For example, I have a combobox where I can show my users. The XAML looks like this:
<ListBox Height="100" HorizontalAlignment="Left" Margin="6,44,0,0"
Name="listBox_detected" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Path=ServiceLogic.Users}" />
When I run the application, the list remains emtpy. What else do I need to do to get that information in my list?
You need to change a few things to make this work in your scenario:
Set the correct DataContext for your window:
public MainWindow()
{
InitializeComponent();
DataContext = new ServiceLogic();
}
Make sure that ServiceLogic has a public property named Users:
public List<User> Users { get; set; }
if you want to add/remove items to this List at runtime, consider using an ObservableCollection<T> as this will notify the UI of any changes automatically.
Update the binding logic of your xaml, so that you bind to the correct list. Also set the DisplayMemberPath property or add a template so that the objects are displayed nicely:
<ListBox ItemsSource="{Binding Path=Users}" DisplayMemberPath="Name"/>
or
<ListBox ItemsSource="{Binding Path=Users}">
<ListBox.ItemTemplate>
<DataTemplate>
<...your data template, like grid or stackpanel/>
</DataTemplate>
</ListBox.DataTemplate>
When using DisplayMemberPath, make sure the User-class has the correct properties. Add the following to User.cs:
public string Name
{
get { return _name; }
set { _name = value; }
}
Here ItemsSource="{Binding Path=ServiceLogic.Users}" you state that data has public property ServiceLogic
Second, you data is acquired through DataContext
Change constructor:
public MainWindow()
{
InitializeComponent();
serviceLogic = new ServiceLogic ();
DataContext = serviceLogic;
}
and change binding to this one:
<ListBox Height="100" HorizontalAlignment="Left" Margin="6,44,0,0"
Name="listBox_detected" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Path=Users}" />
In Binding I removed ServiceLogic because SL stands as data item. And Path - is the path of the property.
I think you need to set "DisplayMemberPath" property of ListBox.

Categories