Changing the View for a ViewModel
Less words more code.
As far as you said, you have the class SampleViewModel
. I added the property Title
for demonstration and ViewType
for identifying the correct view:
public enum ItemViewType { View1, View2 };
public class SampleViewModel
{
public string Title { get; set; }
public ItemViewType ViewType { get; set; }
}
The DataTemplateSelector
for two views depending on the ViewType
property:
class ItemViewTemplateSelector : DataTemplateSelector
{
public DataTemplate View1Template { get; set; }
public DataTemplate View2Template { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var vm = item as SampleViewModel;
if (vm == null)
return null;
switch (vm.ViewType)
{
case ItemViewType.View1:
return View1Template;
case ItemViewType.View2:
return View2Template;
}
return null;
}
}
Xaml code:
<Window.Resources>
<DataTemplate x:Key="view1Template">
<TextBlock Text="{Binding Title}" Foreground="Red"/>
</DataTemplate>
<DataTemplate x:Key="view2Template">
<TextBox Text="{Binding Title}" />
</DataTemplate>
<local:ItemViewTemplateSelector x:Key="viewTemplateSelector"
View1Template="{StaticResource view1Template}"
View2Template="{StaticResource view2Template}"/>
</Window.Resources>
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<StackPanel>
<Button Content="ChangeView" HorizontalAlignment="Center" Command="{Binding SwitchViewCommand}"/>
<ContentControl Content="{Binding ItemViewModel}" ContentTemplateSelector="{StaticResource viewTemplateSelector}"/>
</StackPanel>
The main part is in the class MainViewModel
where I've put the logic for switching views:
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
this.ItemViewModel = new SampleViewModel { Title = "Some title", ViewType = ItemViewType.View1 };
this.SwitchViewCommand = new RelayCommand(() =>
{
this.ItemViewModel.ViewType = this.ItemViewModel.ViewType == ItemViewType.View1
? ItemViewType.View2
: ItemViewType.View1;
//The magic senquence of actions which forces a contentcontrol to change the content template
var copy = this.ItemViewModel;
this.ItemViewModel = null;
this.ItemViewModel = copy;
});
}
public RelayCommand SwitchViewCommand { get; set; }
private SampleViewModel itemViewModel;
public SampleViewModel ItemViewModel
{
get { return itemViewModel; }
set
{
itemViewModel = value;
RaisePropertyChanged("ItemViewModel");
}
}
}
The SwitchViewCommand
can be any type of command, I use the command from the mvvmlight library.
Inside the handler of the command I change the type of viewmodel and update the property ItemViewModel
in a tricky way because a ContentControl
refreshes a view only if to change the Content property, and this property will not be changed unless you set a reference to different object.
I mean, even the code this.ItemViewModel = this.itemViewModel
will not change the view.
It's strange, but the workaround doesn't require much work.
Change view via ViewModel
Thanks to Gorky's respond I figured out how to do that.
In Fragment I created observer
sharedViewModel.changeView.observe(viewLifecycleOwner, Observer<Boolean> { hasFinished ->
if (hasFinished) nextFragment()
})
I created changeView variable in ViewModel. When
var changeView = MutableLiveData<Boolean>()
change to true, observer call function.
source:
https://developer.android.com/codelabs/kotlin-android-training-live-data#6
How to change something in View by ViewModel
Your question seems a little un-thought through... by changing a property in the view model, I can make just about anything happen in the view, through the use of IValueConverter
s, DataTrigger
s, Attached Properties, etc. For example, you don't need to use either a DependencyProperty
or an Attached Property to focus a UI element. You can do that with a plain bool
property in the view model and a DataTrigger
like this:
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding PropertyName}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding
RelativeSource={RelativeSource Self}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
So really, you seem to be asking what is possible in WPF and I don't think that that is a reasonably scoped (or on-topic) question to ask here. To find that out, you'd be better off reading through the Walkthrough: Getting Started with WPF page on MSDN.
However, I think that you might actually be asking How to change something in View by ViewModel [specifically when using MVVM]. In that case, I can think of another used element: the humble delegate
, which enables us to pass values between related view models, or in your case, from the view model to the view.
Using this mechanism, we can literally [indirectly] call UI methods in views from the view model, so as I said earlier... we can do just about anything - MVVM does not stop us from using anything that WPF provides.
Incidentally, why are you using DependencyProperty
s in your view model? You shouldn't have any declared in your view model, because they are for UI elements... in view models, you can just implement the INotifyPropertyChanged
interface and use plain old CLR properties with exactly the same functionality, but much less complexity.
UPDATE >>>
As mentioned, the DependencyProperty
is a UI class, meant for UI controls, such as a UserControl
. They are total overkill for use in view models, because as mentioned, we can use plain CLR properties there instead. If you look at the DependencyProperty
Class page on MSDN, you'll be able to see how many hundreds of public members that it has... why pay for all of these (in RAM) when you're not using any of them?
The INotifyPropertyChanged
interface doesn't add anything more than a DependencyProperty
, but instead offers the same property notification access, but for a greatly reduced cost (no hundreds of unused properties). I can tell by the way that you said that all of your view models extend the DependencyObject
class, that you have misunderstood the famous error below:
A
Binding
can only be set on aDependencyProperty
of aDependencyObject
I confess that it is a misleading error and does confuse new WPF users. So, my advice to you is for you to implement ordinary CLR type properties in your view model and to not extend the DependencyObject
class... you will then also be able to remove your UI related dll using
s. Furthermore, when using CLR properties, you will need to correctly implement the INotifyPropertyChanged
interface in order to 'plug them into' the WPF property change notification framework.
Now, you asked how to use delegate
s to pass data from the view model to the view... unfortunately, that is another whole question on its own and I have just about run out of time on this question. So instead of repeating the whole story again, I'd prefer to suggest that you read my answers to the Passing parameters between viewmodels and How to call functions in a main view model from other view models? questions here on Stack Overflow for explanations and code examples.
With all due respect, I'd appreciate it if you don't leave further comments asking for more information on this matter as this answer is really long enough now. If you do have any further questions, please ask a new question and provide as much code/information as possible.
Changing View from ViewModel in View-Base WPF Application
In MVVM Light
there is a feature called Messenger
. You could use this to communicate between ViewModel
s.
Here is an answer with an example of that: MVVM Light Messenger - Sending and Registering Objects
SwiftUI: Change view @State property from ViewModel
You can directly bind to the @Published
property with just:
$viewModel.startTime
Which will be of type Binding<Date>
, even though we declare it as just a @Published
property on BookTimeViewModel
.
Example code:
class BookTimeViewModel: ObservableObject {
@Published var startTime: Date = .now
}
struct BookTimeView: View {
@ObservedObject var viewModel: BookTimeViewModel
var body: some View {
DatePicker("pick time", selection: $viewModel.startTime, displayedComponents: .hourAndMinute)
}
}
Similar to my answer here.
Save Changes of View in Viewmodel?
You can make the Mode=TwoWay and use UpdateSourceTrigger=Explicit. Refer below code.
<StackPanel>
<TextBox Name="tb_1" Text="HelloWorld!"/>
<TextBox Name="tb_2" Text="{Binding Path=Text, ElementName=tb_1, Mode=TwoWay,UpdateSourceTrigger=Explicit}"/>
<Button Content="Update Source" Click="Button_Click"/>
</StackPanel>
Changing ViewModel Object does not reflect the changes to the View
So apparently, I had to do two things to make it work.
Since I was binding the User Control to itself like this...
this.DataContext = this;
I had to implement INotifyPropertyChange
on the User Control class itself and implement the PropertyChanged
call on the CurrentViewModel
property, like this:
private PageSetupEditorViewModel _currentViewModel;
public PageSetupEditorViewModel CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
OnPropertyChanged(nameof(CurrentViewModel));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
But that did not entirely updated the view as I was using a collection to generate tabs in the TabControl
.
I had to refresh the view for TabComtrol.Items
using this on the selection of ComboBox
's selection change event, like this:
tcOrientation.Items.Refresh();
This solved the problem for me :)
Change the view element property based on ViewModel property change
Change the style triggers to:
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsBlueColor}" Value="True">
<Setter Property="Foreground" Value="RoyalBlue"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsBlueColor}" Value="False">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
Check implementation of INotifyPropertyChanged in ViewModel.
this works in a sample for me.
Sample :
class LabelViewModel:INPC
{
public event PropertyChangedEventHandler PropertyChanged=new delegate{};
bool _isBlueColor;
public bool IsBlueColor
{
get
{
return _isBlueColor;
}
set
{
_isBlueColor=value;
OnPropertyChange();
}
}
public OnPropertyChange([CallerMemberName] string propname="")
{
PropertyChanged.Invoke(this,ew PropertyChangedEventArgs(propname));
}
}
Create an instance of LabelViewModel(labelVM) & assign to label1.DataContext=labelVM;
Related Topics
How to Convert Date Format to Dd-Mm-Yyyy in C#
Data Binding Directly to a Store Query (Dbset, Dbquery, Dbsqlquery) Is Not Supported
Get Supported Characters of a Font - in C#
Expression-Bodied Function Members Efficiency and Performance in C# 6.0
How to Add Openidconnect via Identityserver4 to ASP.NET Core Serverside Blazor Web App
Show Yes/No Instead True/False in Datagridview
C# Date Formatting Is Losing Slash Separators
How to Check If a String Is a Number
How to Use Class Name as Parameter in C#
Add Vertical Scroll Bar to Panel
Regex Word Boundary Expressions
How to Get the Subscribers of an Event
Why Cannot I Cast My Com Object to the Interface It Implements in C#
String.Equals() Not Working as Intended
How to Determine If an Event Is Already Subscribed
Using Appdomain in C# to Dynamically Load and Unload Dll
Client-Side Validation in Custom Validation Attribute - ASP.NET MVC 4