I've added an observable data and bound it to my data grid as follows.
private ObservableCollection<Order> _allOrders;
public ObservableCollection<Order> AllOrders
{
get { return _allOrders;}
set { _allOrders = value; OnPropertyChanged(); }
}
public Presenter() { _allOrders = new ObservableCollection<Order>(...); }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] String propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
When I set breakpoint on the event that is supposed to filter the data, I set the property AllOrder to null. I can verify using the watch that it's set to that. However, the view isn't updated, so I'm guessing that I forgot something. The view model class Presenter implements INotifyPropertyChanged interface, of course.
What's missing?
Edit
The XAML code for the grid looks as follows.
<DataGrid x:Name="dataGrid"
ItemsSource="{Binding AllOrders}"
AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn" ...>
Assuming that you set DataContext accordingly and AllOrders binding works initially if you want to filter items in the UI, without change collection, it's much easier when you use ListCollectionView with a Filter. WPF does not bind directly to collection but to a view - MSDN.
private readonly ObservableCollection<Order> _allOrders;
private readonly ListCollectionView _filteredOrders;
public ICollectionView FilteredOrders
{
get { return _filteredOrders; }
}
public Presenter()
{
_allOrders = new ObservableCollection<Order>(...);
_filteredOrders = new ListCollectionView(_allOrders);
_filteredOrders.Filter = o => ((Order)o).Active;
}
and in XAML
<DataGrid ... ItemsSource="{Binding FilteredOrders}">
when you want to manually refresh UI just call Refresh
_filteredOrders.Refresh();
Apart from that nothing changes in the view model. You still add/remove items to _allItems and changes should be picked up automatically by UI
Do you set the property AllOrders only in the constructor? If so, then do not set the field _allOrders but the property AllOrders. If you set the field then notification is never raised.
Related
Databinding is a difficult concept that I still can't quite grasp despite reading through dozens of topics already.
I want to have a TextBox that would change its text every time a field 'status' is being changed in my code-behind, for debugging purposes.
Here's what I have so far:
public partial class ReviewsControl : UserControl
{
private Status status = MainControl.AppStatus;
public string StatusDisplay
{
get
{
return status.ToString();
}
}
}
And here's my take on the view:
<TextBlock x:Name="StatusBlock" HorizontalAlignment="Left" Margin="10,450,0,0" TextWrapping="Wrap" Text="{Binding StatusDisplay, Source=ReviewsControl, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Height="40" Width="205"/>
The code above doesn't even show anything, let alone do that dynamically. What should I change if I want XAML to detect changes in the in my C# code and change the textbox accordingly?
I too had issues early on. Your view (display to end-users) does not care how or where things come from, you just know what will be exposed in your View Model controller to bind to. To have things updated in your view, the most common is binding and having your view model include the INotifyPropertyChanged interface. This is so you can force raising the event when a property changes, whatever is "listening" for it will update itself..
Simple class, you can just grab from the : INotify, the event exposed to allow things to get registered to, and your method to actually RAISE the event to pass up stream to those listening for changes.
public class SomeBaseClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Once that is done, your going to expose properties in your class by making as public with getter/setters. You don't bind to a field.
public string SomeThingYouWantToExpose {get; set; }
And in your code, however you are getting data, label refresh, whatever, you would set and raise the property-changed
public void GettingSomeData()
{
// … doing something to get / prepare / whatever...
SomeThingYouWantToExpose = "some new label";
// Now raise which the view bound to this property will updated itself
RaisePropertyChanged( "SomeThingYouWantToExpose" );
}
Now, in your view, you would bind to whatever your view model object is and then the property on the class. Don't know if you specifically need the x:Name reference which basically makes this a field in your control. Not necessary in this example unless you are trying to bind other controls in same display as a result of this field..
<TextBlock Height="40" Width="205" Margin="10,450,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top"
DataContext="{Binding YourViewModelObjectName}"
Text="{Binding SomeThingYouWantToExpose}"
TextWrapping="Wrap" />
Hopefully these pieces within your scenario will make sense and move you forward in your project. Any additional clarification, let me know.
CLARIFICATION on the DATA CONTEXT BINDING
The way I have implemented in my apps, I would have a
MyView -- via the visual declaration... be it a window, grid, complex user control with many controls, whatever...
MyDataModel - a class that is used to query data from whatever data source, such as SQL-Server.
MyView_ViewModel - a custom class that has the INotifyPropertyChanged incorporated and where I expose different properties and other objects I want to expose / make available to the view
So, in the MyData_ViewModel, I would create the view and also create my view model. After creating the view, I would set the overall view's DataContext to the "MyView_ViewModel"
public class MyData_ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public void LoadingMyDataAndView()
{
// Controller I use to get whatever data I need from ex: SQL
_myDataModel = new MyDataModel();
// the view / window / control I want to present to users
_myView = new MyView();
// Now, set the data context on the view to THIS ENTIRE OBJECT.
// Now, anything on THIS class made as public can be have a binding
// directly to the control in the view. Since the DataContext is
// set here, I can bind to anything at this level or lower that is public.
_myView.DataContext = this;
}
private MyView _myView;
private MyDataModel _myDataModel;
// such as example exposed property
public string SomeThingYouWantToExpose {get; set; }
public void GettingSomeData()
{
var something = _myDataModel.GetSomeData();
// … doing something to get / prepare / whatever...
SomeThingYouWantToExpose = "some new label";
// Now raise which the view bound to this property will updated itself
RaisePropertyChanged( "SomeThingYouWantToExpose" );
}
}
Hopefully this EXAMPLE shows how the pieces tie together. The view would no longer need the individual DataContext set since the whole view is set, just needs to bind to the individual public property.
Assuming that the TextBlock is a child element of the UserControl, i.e. that
<TextBlock x:Name="StatusBlock" Text="{Binding StatusDisplay}" ... />
is declared in the UserControl's XAML, the Binding's RelativeSource should be set to the parent UserControl like this:
<TextBlock Text="{Binding StatusDisplay,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
Since StatusDisplay is a property of a UserControl, i.e. a DependencyObject, it should be declared as a dependency property:
public partial class ReviewsControl : UserControl
{
public static readonly DependencyProperty StatusDisplayProperty =
DependencyProperty.Register(
nameof(StatusDisplay), typeof(string), typeof(ReviewsControl);
public string StatusDisplay
{
get { return (string)GetValue(StatusDisplayProperty); }
set { SetValue(StatusDisplayProperty, value); }
}
}
I am trying to keep my question simple and to the point.
At the moment, if I have a property that updates the underlying Model data, and it therefore needs to inform a few other properties that the source has changed, I do it like this:
public Data.MeetingInfo.Meeting Meeting
{
get { return _Meeting; }
set
{
if(value != null)
{
_Meeting = value;
if (_Meeting.IsDirty)
{
_Model.Serialize();
_Meeting.MarkClean();
OnPropertyChanged("Meeting");
OnPropertyChanged("BibleReadingMain");
OnPropertyChanged("BibleReadingClass1");
OnPropertyChanged("BibleReadingClass2");
}
}
}
}
private Data.MeetingInfo.Meeting _Meeting;
As you can see, I added several different OnPropertyChanged method calls. Is this an acceptable way to do it? Or, can the specific properties in the Model inform the View that some of it's source has changed?
I have read about implementing the same OnPropertyChanged features in the Model classes. Thus the XAML will pick it up. But I thought those two parts of the MWWV we not supposed ot know about each other.
The thing is, the other 3 are in disabled controls, but they can be updated from two places on the window. So I don't think I can have two update source triggers can I?
Thank you.
Second attempt at explainign things:
ObservableCollection of Meeting objects. Bound to a ComboBox:
<ComboBox x:Name="comboMeetingWeek" ItemsSource="{Binding Meetings}"
SelectedItem="{Binding Meeting, UpdateSourceTrigger=PropertyChanged}" />
The Meeting object contains several properties. We bind controls on the window with these properties. Example:
<ComboBox x:Name="comboNotes" IsEditable="True"
DataContext="{Binding Meeting}"
Text="{Binding Note, UpdateSourceTrigger=LostFocus}"
ItemsSource="{StaticResource Notes}"/>
I do this for the majority of the controls. So the Meeting property in the view model is kept up to date and then when you select a different meeting it commits it to the model data and displays the new meeting (as previously described).
But, in some places on the window, I have some disabled text boxes. These are associated with properties nested inside the Meeting object. For example:
<TextBox x:Name="textBibleReadingMain" Grid.Column="0" Margin="2" IsEnabled="False"
DataContext="{Binding TFGW.BibleReadingItem.Main}"
Text="{Binding DataContext.BibleReadingMain, ElementName=oclmEditor, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"/>
The parent TabItem already has it's DataContext set to {Binding Meeting}. What we need to display in the text box is:
Meeting (current context).TFGW.BibleReadingItem.Main.Name
This is why I had to do it has I did. For the above text box, this is what I want to allow to happen:
It should display the content of Meeting.TFGW.BibleReadingItem.Main.Name (Meeting already being a bound property).
As you select a different meeting from the dates combo, this text box should update.
If the user selects a name from the DataGrid and the ActiveAstudentAssignmentType combo is set to StudentAssignmentType::BibleReadingMain then I also want to update the text box.
I think what I am getting confused about is when I am supposed to derive my classes from INotifyPropertyChanged. My Model data is the Meeting objects with it's own data. Should all of these be inheriting from INotifyPropertyChanged and raising OnPropertyChanged? At the moment I do not have that implemented anywhere. I tell a lie, the only place I implemented it was for the view model itself:
public class OCLMEditorViewModel : INotifyPropertyChanged
So that is why I had to do it the way I did.
Any clearer?
Based on all the comments and further reasearch ....
One of the answers stated:
Viewmodel is created and wraps model
Viewmodel subscribes to model's PropertyChanged event
Viewmodel is set as view's DataContext, properties are bound etc
View triggers action on viewmodel
Viewmodel calls method on model
Model updates itself
Viewmodel handles model's PropertyChanged and raises its own PropertyChanged in response
View reflects the changes in its bindings, closing the feedback loop
I also read a bit of this (which confused me somewhat) where it stated:
The Model notifies the ViewModel if the data in the underlying data store has changed.
So, the first thing I did was change my Meeting object to derive from INotifyPropertyChanged. In addition, I added new properties for gaining access to deeper data in the Meeting model. Example (stripped down):
public class Meeting : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
#region Bible Reading Name Properties
[XmlIgnore]
public string BibleReadingMainName
{
get { return _TFGW.BibleReadingItem.Main.Name; }
set
{
_TFGW.BibleReadingItem.Main.Name = value;
OnPropertyChanged("BibleReadingMainName");
}
}
[XmlIgnore]
public string BibleReadingClass1Name
{
get { return _TFGW.BibleReadingItem.Class1.Name; }
set
{
_TFGW.BibleReadingItem.Class1.Name = value;
OnPropertyChanged("BibleReadingClass1Name");
}
}
[XmlIgnore]
public string BibleReadingClass2Name
{
get { return _TFGW.BibleReadingItem.Class2.Name; }
set
{
_TFGW.BibleReadingItem.Class2.Name = value;
OnPropertyChanged("BibleReadingClass2Name");
}
}
#endregion
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
In my ViewModel I set it as a listener for PropertyChanged:
_Meeting.PropertyChanged += Meeting_PropertyChanged;
At this point in time, the handler just relays the property that was changed:
private void Meeting_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged(e.PropertyName);
}
In my XAML, I adjust my TextBox to work with the new property, and I remove the DataContext reference. So I now have:
<TextBox x:Name="textBibleReadingMain" Grid.Column="0" Margin="2" IsEnabled="False"
Text="{Binding BibleReadingMainName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
ON the right, where I have the DataGrid, when we click a row and the SelectedStudentItem is updated, we can now do:
private Student _SelectedStudentItem;
public Student SelectedStudentItem
{
get
{
return _SelectedStudentItem;
}
set
{
// We need to remove this item from the previous student history
if (_SelectedStudentItem != null)
_SelectedStudentItem.History.Remove(Meeting.DateMeeting);
_SelectedStudentItem = value;
if (_SelectedStudentItem == null)
return;
_EditStudentButtonClickCommand.RaiseCanExecuteChanged();
_DeleteStudentButtonClickCommand.RaiseCanExecuteChanged();
OnPropertyChanged("SelectedStudentItem");
if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingMain)
_Meeting.BibleReadingMainName = _SelectedStudentItem.Name;
else if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingClass1)
_Meeting.BibleReadingClass1Name = _SelectedStudentItem.Name;
else if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingClass2)
_Meeting.BibleReadingClass2Name = _SelectedStudentItem.Name;
}
Based on the current ActiveStudentAssignmentType value we can directly update the source property. Thus the TextBox will automatically know about it due to the PropertyChange listener.
Thus, the original Meeting property code now looks like this:
public Data.MeetingInfo.Meeting Meeting
{
get { return _Meeting; }
set
{
// Has the existing meeting object changed at all?
if(_Meeting != null && _Meeting.IsDirty)
{
// Yes, so save it
_Model.Serialize();
_Meeting.MarkClean();
}
// Now we can update to new value
if (value != null)
{
_Meeting = value;
OnPropertyChanged("Meeting");
}
}
}
private Data.MeetingInfo.Meeting _Meeting;
All of those extra OnPropertyChanged calls are now obsolete!
The thing I was missing was implementing Notification from the Model to the ViewModel. And then the ViewModel informing the View.
I have a question, I am building small login system, basically its ready and working, but still having some problems with UI, si if I take such button click action
private void LoadButton_Click(object sender, RoutedEventArgs e)
{
Nullable<bool> creditencialFile = _controls.CredencialsFileDialog.ShowDialog();
if (creditencialFile == true)
{
ContextStatic.Filename = _controls.CredencialsFileDialog.FileName;
FileInfo creditencialsFileInfo = new FileInfo(ContextStatic.Filename);
ContextStatic.RootFolder = creditencialsFileInfo.DirectoryName;
model.LeapCreditencials = CredentialHelper.LoadCredentials(ContextStatic.Filename);
}
}
It loads credentials from file, and they are saved in object attribute:
model.LeapCreditencials = CredentialHelper.LoadCredentials(ContextStatic.Filename);
Now i want to refresh or reload UI so I all information windows would be set up to with new info. Question is should I need to reload per one control, or there is a smart way to reload Ui with new object values?
Yes, you should implement INotifyPropertyChanged in your model
msdn description to implement INotify Interface
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
When value of model is changed it will reflect in the UI.
Xaml
<TextBox Text="{Binding Mymodel.CustomerName,
UpdateSourceTrigger=PropertyChanged}" />
Model
public class DemoCustomer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
Yeah, there's a smart way. It's called MVVM (a.k.a. Model View View Model). It is not so hard to understand. You just bind your view to values in ViewModel, and when a value is changed UI is automatically updated.
I have two DataGrid, each binding in a dataSource like this :
ItemsSource="{Binding Data, ElementName=EmpSource, Mode=TwoWay}"
The first DataGrid(dgJob), contains Job and the second(dgEmp), the employee linked to the job.
I want to keep all the employees in the EmpSource, and display in the dataGrid, only those who are linked to the selected job in my first datagrid.
So I am doing this in the dgJob selectionChanged event :
dgEmp.ItemsSource = null;
var lstEmp = EmpSource.DataView.OfType<Emp>().Where(ores => ores.IdJob == itmJobSelect.IdJob).ToList();
dgEmp.ItemsSource = lstEmp;
The problem is, the dataGrid is not clearing when I change the selected line in my datagrid with the jobs, so for every job, I display every Employees in the dgEmp, while I should only display those who are connected to the job.
I can delete the line in the xaml, that determine the dataSource, but if I do this, I must refresh the dataGrid when there is a change in the dataSource.
But I don't found how to refresh it(at least for the first time) unless I write the 3 lines each time after a change in dataSource.
Can somebody help me find a solution to my problem?
Thank you.
I recommend you to use MVVM design pattern. You should load your data in view model class and store it in collection which implements INotifyCollectionChanged interface. View model should also implement INotifyPropertyChanged interface.
When your employee collection changes, you should filter second collection as in following code:
Jobs.CollectionChanged += (sender, args) =>
{
Employees = AllEmployees.Where(c=> c.IdJob == SelectedJob.IdJob);
}
You should also do same thing when SelectedJob changes and DataGrid will be refreshed.
This will work only when you will have implemented property changed notifications and correct binding was specified.
Here's example of property changed implementation which you should write:
public class ViewModel : INotifyPropertyChanged
{
public IEnumerable<Emp> Employees
{
get { return _employees; }
set
{
if (_employees != value)
{
_employees = value;
OnPropertyChanged("Employees");
}
}
}
/* ... */
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You should also assign your view model instance to DataContext for make binding works. For example in code behind file constructor:
public void Page()
{
DataContext = new ViewModel();
InitializeComponent();
}
I'm having trouble binding to the text property of a combobox. It seems like it doesn't bind until I select something in the combobox. Then it works fine.
Here is the code straight from a test app:
View
<ComboBox ItemsSource="{Binding ListItems}"
Text="{Binding Test}" />
ViewModel
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> ListItems { get; set; }
public ViewModel()
{
ListItems = new ObservableCollection<string>();
ListItems.Add("Southwest");
ListItems.Add("South");
}
public string Test
{
get { return "South"; }
set { PropertyChanged(this, new PropertyChangedEventArgs("Test")); }
}
}
However, when I reverse the order of the observable collection items, everything works fine.
ListItems.Add("South");
ListItems.Add("Southwest");
What's going on here?
The text property doesn't work like this.
Read this document:
http://msdn.microsoft.com/en-us/library/system.windows.controls.combobox.text.aspx
Like suggested by hameleon86 use the selecteditem instead.
I think it Works if you reverse the order of your collection maybe because the Text property take the first item of the collection by default
I think you might want to do :
PropertyChanged(this, new PropertyChangedEventArgs("ListItems"));
After you inserted the element.