Prism 5.0 wrap Model Attributes in ViewModel - c#

Hello I am relatively new with Prism but here is my question:
I am currently using Prism 5 with WPF. I have created a model, viewmodel and view. The View gets updated when an attribute from model changes. My problem is: when I want to process the Attribute from the model(for example another attribute name), the view does not get updated. Heres my Code. I'll be very thnakful if you could help me.
When RunTinting in the model Changes, the view does not get updated.
Model
public class MyModel :BindableBase
{
private Boolean _RunTinting;
public Boolean RunTinting
{
get { return _RunTinting; }
set { SetProperty(ref _RunTinting, value); }
}
Viewmodel
public class MainWindowViewModel : BindableBase
{
private MyModel model;
public MyModel Model
{
get { return this.model; }
set { SetProperty(ref this.model, value); }
}
public MainWindowViewModel()
{
this.Model=new MyModel();
}
public Boolean RunTinting2
{
get { return this.model.RunTinting; }
set { SetProperty(ref this._RunTinting, value); }
}
}
XAML
<Label x:Name="label1_Copy11" Content="{Binding RunTinting2}" HorizontalAlignment="Left" Margin="366,320,0,0" VerticalAlignment="Top" Height="25" Width="85" >

If you set the model property dynamically the view should bind to this property instead of the view model property:
<Label x:Name="label1_Copy11" Content="{Binding Model.RunTinting}" HorizontalAlignment="Left" Margin="366,320,0,0" VerticalAlignment="Top" Height="25" Width="85" >
Because the view model won't raise any change notifications when a property of the model is set to a new value.
The other option is to set the model property through the wrapper property of the view model, i.e. instead of setting the model property directly you set the view model property. Make sure that you then set the model's property in the setter of the wrapper property and that you raise a PropertyChanged event for the view model property that the view binds to:
public class MainWindowViewModel : BindableBase
{
...
public Boolean RunTinting2
{
get { return this.model.RunTinting; }
set { this.model.RunTinting = value; OnPropertyChanged("RunTinting2"); }
}
}
<Label x:Name="label1_Copy11" Content="{Binding RunTinting2}" HorizontalAlignment="Left" Margin="366,320,0,0" VerticalAlignment="Top" Height="25" Width="85" >

this is how I solved this problem, thank you Rachel and Will.
ViewModel
public void InitModel()
{
Model.PropertyChanged += Model_PropertyChanged;
}
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "RunTinting")
{
OnPropertyChanged("RunTinting2");
}
}
#mm8, I tried your solution, but it didnt work... but thank you anyways for the time taken.
I do not know why my answer has negative ratings. It works and respect the mvvm model!

Related

How do you bind an observable collection to a listview located in a modal/window?

I have a wpf application that I want to be able to launch a separate window in which I will have a listview bound to an observable collection. However I am unable to get the collection values to appear in the list view. Here is some of the code.
Window (Named WizardView):
(Data context defined like so at top of xaml):
d:DataContext="{d:DesignInstance Type=viewModels:MainViewModel}"
<Border Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" BorderBrush="Black">
<ListView BorderThickness="0" ItemsSource="{Binding TestModel.FailedTests}">
<Label Content="Introduction" FontWeight="Bold" />
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding }"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
MainViewModel Code:
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
TestModel = new TestViewModel();
WizardModel = new WizardViewModel(TestModel);
}
private WizardViewModel _wizardModel;
public WizardViewModel WizardModel
{
get
{
return _wizardModel;
}
set
{
_wizardModel = value;
RaisePropertyChanged();
}
}
private TestViewModel _testViewModel;
public TestViewModel TestModel
{
get
{
return _testViewModel;
}
set
{
_testViewModel = value;
RaisePropertyChanged();
}
}
WizardViewModel Code:
public class WizardViewModel : TestViewModel
{
internal TestViewModel TestModel;
public WizardViewModel(TestViewModel testModel)
{
TestModel = testModel;
(TroubleShootCommand is defined in seperate UC, and launches fine)
TestModel.TroubleShootCommand = new DelegateCommand(Load, CanTroubleShoot);
}
public void Load()
{
(Sync Root is used because it is running on worker thread. Issue somewhere here?)
_syncRoot.Send(o =>
{
var troubleShootWizard = new WizardView();
troubleShootWizard.Owner = Application.Current.MainWindow;
troubleShootWizard.ShowDialog();
}, null);
}
Observable Collection in TestViewModel (Initialized in ctor):
private ObservableCollection<string> _failedTests;
public ObservableCollection<string> FailedTests
{
get { return _failedTests; }
set
{
_failedTests = value;
RaisePropertyChanged();
}
}
Any Help is appreciated, I feel like I have tried everything. I have watched values through the watch window under TestModel.FailedTests in the collection right before and right after launch.
First,
(Data context defined like so at top of xaml): d:DataContext="{d:DesignInstance Type=viewModels:MainViewModel}"
This is a mistake, this way d: you are defining the DataContext at design time..
You can create the viewmodel inside .xaml this way:
<WizardView.DataContext>
<viewModels:MainViewModel/>
</WizardView.DataContext>
Using the design time declaration can help in many ways like knowing the viewmodel in case you are creating it and assigning it in C# (or via a IoC mechanism), also it helps tools like IntelliSense and ReSharper to analyze your bindings and warn you if you misspell a property's name in xaml, auto-completion, etc... (more on this can be found here, and here)
Second, if you are assigning the WizardViewModel in your .xaml the same way (i.e. design-time), then you can either do it in your Load() function (add troubleShootWizard.DataContext = this;) or assign it in .xaml the same way I've mentioned before.

Binding WPF Grid to a model

I have a MVVM application which has a WPF Grid which contains other embedded WPF Grids and at the same time, each of them contain some fields (WPF TextBlocks).
Very simplified example - View:
<Grid>
<Grid>
// Row definitions
// Colum definitions
<TextBlock Grid.Row="3" Grid.Column="0"
Text="{Binding Path=SomeField1}" />
<Grid>
<Grid>
// Row definitions
// Colum definitions
<TextBlock Grid.Row="0" Grid.Column="1"
Text="{Binding Path=SomeField2}" />
<Grid>
</Grid>
Each of these TextBlocks are bound to a string properties defined in view model.
View model (It implements INotifyPropertyChanged):
private string _someField1;
public string SomeField1
{
get return _someField1;
set
{
if (_someField1 == value) return;
_someField1 = value;
OnPropertyChanged("SomeField1");
}
}
private string _someField2;
public string SomeField2
{
get return _someField2;
set
{
if (_someField2 == value) return;
_someField2 = value;
OnPropertyChanged("SomeField2");
}
}
Then I have a model, I mean, a class with some public properties that is filled in by one process once data is obtained from a device. This class contains exactly the same properties as those defined in the view model.
Model:
public class MyModel
{
private string _someField1;
public string SomeField1
{
get return _someField1;
set
{
if (_someField1 == value) return;
_someField1 = value;
}
}
private string _someField2;
public string SomeField2
{
get return _someField2;
set
{
if (_someField2 == value) return;
_someField2 = value;
}
}
}
Later from view model I extract the data from this class (model), and I assign the values of those properties to the matching properties in view model. Finally, since view is bound to these properties, then view is correctly updated with values as below example.
View model method which extracts data received:
private void DataReceived(MyModel data)
{
this.SomeField1= data.SomeField1;
this.SomeField2= data.SomeField2;
}
The problem is that I have to define twice the properties, in view model and model. So I want to avoid this, I would like to bind Textblocks directly to properties in model and not defined the properties in view model to avoid redundant code. Or for example, is there any easy way to bind my model (MyModel) to the outer main grid and then textboxes bound to the properties in the view model (similar when bound itemsource in datagrid)?
I would suggest a generic view model:
public class BaseViewModel<TModel>
{
public TModel Model
{
get;
private set;
}
public BaseViewModel(TModel model)
{
this.Model = model;
}
}
Then you can bind to it:
<TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding Path=Model.SomeField1}" />
I was thinking if below it is ok and respects MVVM pattern. I have thought it after seeing solution proposed by c0d3b34n. I think it is simpler and no need to do interfaces and generic view model. I have checked and it works:
Declare a property in view model:
private MyModel _model;
public MyModel Model
{
get { return _model; }
set
{
_model = value;
OnPropertyChanged("Model");
}
}
Then in the view:
<TextBlock Grid.Row="3" Grid.Column="0" Text="{Binding Path=Model.SomeField1}" />
... and the same for the rest of TextBlocks.
Finally:
private void DataReceived(MyModel data)
{
this.Model = data;
}
But as said by BionicCode in comments, this solution breaks MVVM pattern.

Binding model with multiple properties in UserControl using one DependencyProperty [duplicate]

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
Closed 4 years ago.
I would like to be able to bind complex model (many properties) to UserControl through DependencyProperty, and if model would be edited in UserControl I would like to see this edited information inside my binded model.
Example application: Model, UserControl (xaml + cs), MainWindow (xaml + cs). I have no ViewModel to simplify idea.
Model:
public class MyModel : INotifyPropertyChanged
{
private string _surname;
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
}
}
public string Surname
{
get => _surname;
set
{
_surname = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MyModelEditor.xaml (inside Grid):
<DockPanel>
<TextBox Text="{Binding MyModel.Name}"/>
<TextBox Text="{Binding MyModel.Surname}"/>
</DockPanel>
Also contains this line in UserControl root element:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
MyModelEditor.xaml.cs:
public partial class MyModelEditor : UserControl
{
public MyModel MyModel
{
get => (MyModel)GetValue(MyModelProperty);
set => SetValue(MyModelProperty, value);
}
public static readonly DependencyProperty MyModelProperty =
DependencyProperty.Register("MyModel", typeof(MyModel), typeof(MyModelEditor), new FrameworkPropertyMetadata(null));
public MyModelEditor()
{
InitializeComponent();
}
}
MainWindow.xaml (inside Grid):
<DockPanel>
<Button DockPanel.Dock="Bottom" Content="Press Me!" Click="ButtonBase_OnClick"/>
<controls:MyModelEditor MyModel="{Binding MyModel}"/>
</DockPanel>
MainWindow.xaml.cs:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private MyModel _myModel;
public MyModel MyModel
{
get => _myModel;
set
{
_myModel = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show(MyModel?.Name);
}
}
My test scenario: type text in textbox, press button.
Current behavior: Message after pressing button is empty.
Expected behavior: Message after pressing button is same like in textbox.
I wold not like to bind to all properties separately, because in future I will have much more then two properties.
Why current approach does not work?
How can I achieve my goal?
You are apparently not using the UserControl instance as Binding source in your UserControl's XAML. One way to do this would be to set the Binding's RelativeSource:
<TextBox Text="{Binding MyModel.Name,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
However, you don't need a new dependency property at all for this purpose. Just bind the UserControl's DataContext to a MyModel instance, like
<controls:MyModelEditor DataContext="{Binding MyModel}"/>
The Bindings in the UserControl's XAML would automatically work with the MyModel object, like this:
<DockPanel>
<TextBox Text="{Binding Name}"/>
<TextBox Text="{Binding Surname}"/>
</DockPanel>
For both of your TextBox controls, you should define their Binding with a TwoWay mode (ms docs on binding modes). Which, basically, would assure that the data flow is working in both direction (i.e. from the view model into the view and the other way around):
<DockPanel>
<TextBox Text="{Binding MyModel.Name, Mode=TwoWay}"/>
<TextBox Text="{Binding MyModel.Surname, Mode=TwoWay}"/>
</DockPanel>
As a good practice, you should always explicitly define what is the mode of the the Binding (NOTE: by default it's OneWay TwoWay - how to know which is the default?).
Another tip would be to go ahead and use MvvmHelpers nuget (github project), which could spare you the time of implementing INotifyPropertyChanged. Besides, you shouldn't re-invent the wheel
EDIT: Fixes are in your GitHub repo
Two things to note here
You have not instantiated your ViewModel (i.e. MyModel), so it was always null
You don't need to create DependencyPropery every time you want to pass some information to your UserControl. You could simply bind the DataContext itself

WPF xaml binding properties on textbox cannot clear

I have a textbox binded to one of my viewModel's properties
<TextBox x:Name="box" Height="20" TextWrapping="Wrap" Text="{Binding name}"/>
viewModel.cs:
public string name { get; set; }
[...]
public void clear(){
name = "";
}
AddCommand: Icommand class:
public void Execute(object parameter){
//do some stuff
viewModel.clear();
}
Everything else works perfect. I can read the textbox's and use them to do some calculation in viewModel then bind those calculation to labels to display. But I just cant clear those textbox after I read them. I tried setting the binding to mode=twoway but still doesn't work
You need to tell WPF that the property has changed.
something similar to
private string _name;
public string name
{
get
{
return _name;
}
set
{
_name = value;
PropertyChanged(this, new PropertyChangedEventArgs("name"));
}
}
ofcourse most people would make a base class to avoid having to call that property changed method with so that complicated parameter.

how can i get data from view

i use MVVM to built my project, now i have some troubles,when i click a button, i want get data from view to viewmodel, what should i do?
thanks
Bob
Bind that data to the view model and execute a command when the user clicks the button. The command and data are housed in the view model, so it has everything it needs.
public class YourViewModel : ViewModel
{
private readonly ICommand doSomethingCommand;
private string data;
public YourViewModel()
{
this.doSomethingCommand = new DelegateCommand(this.DoSomethingWithData);
}
public ICommand DoSomethingCommand
{
get { return this.doSomethingCommand; }
}
public string Data
{
get { return this.data; }
set
{
if (this.data != value)
{
this.data = value;
this.OnPropertyChanged(() => this.Data);
}
}
}
private void DoSomethingWithData(object state)
{
// do something with data here
}
}
XAML:
<TextBox Text="{Binding Data}"/>
<Button Command="{Binding DoSomethingWithData}"/>
For information on the various dependencies in the above example such as ViewModel and DelegateCommand, see my series of posts on MVVM.
EDIT after receiving more info: For tracking item selection, simply introduce a view model to represent the item:
public class CustomerViewModel : ViewModel
{
private bool isSelected;
public bool IsSelected
{
get { return this.isSelected; }
set
{
if (this.isSelected != value)
{
this.isSelected = value;
this.OnPropertyChanged(() => this.IsSelected);
}
}
}
}
Your "main" view model would expose a collection of these items (generally an ObservableCollection<T>):
public ICollection<CustomerViewModel> Customers
{
get { return this.customers; }
}
Your view would then bind as:
<ListBox ItemsSource="{Binding Customers}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Notice how each ListBoxItem will have its IsSelected property bound to the CustomerViewModel.IsSelected property. Thus, your main view model can just check this property to determine which customers are selected:
var selectedCustomers = this.Customers.Where(x => x.IsSelected);
The solution suggested by Kent is in my opinion by far the best/only one to follow MVVM.
If however you don't want to replicate/reflect listbox selections to the view model or you want a quick and - according to MVVM - dirty solution, you can use the command parameter to send data from the view to the view model.
For that you have to bind the CommandParameter property of the button to the property which contains the data you want to send to the view model. For simplicity I just used a TextBox.
<StackPanel Orientation="Vertical">
<TextBox x:Name="Data"/>
<Button Content="DoSomething"
Command="{Binding Path=DoSomethingCommand}"
CommandParameter="{Binding ElementName=Data, Path=Text}"/>
</StackPanel>
The ViewModel of the sample looks like the following.
public class ViewModel
{
private ICommand doSomethingCommand = new MyCommand();
public ICommand DoSomethingCommand
{
get
{
return doSomethingCommand;
}
}
}
With this, you will get the specified content as the parameter in the Execute method of ICommand.
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
string dataFromView = (string)parameter;
// ...
MessageBox.Show(dataFromView);
}
}

Categories