I am switching two Views. after message sending from Messenger.Default.Send<Message>(new Message {LoadingIndication="Loaded" },"Token"); It's reciving two message's because it create's OneViewModel two times first time in BinaryMultiViewModel second into OneView. But i need only one message. I can not remove something because in first case it should not switch in second it should show data.
For example
MultiView.cs
namespace Test.ViewModel
{
class BinaryMultiViewModel : ViewModelBase
{
readonly static OneViewModel OneViewModel = new OneViewModel();
readonly static FourViewModel FourViewModel = new FourViewModel();
private ViewModelBase currentMultiViewModel;
public BinaryMultiViewModel()
{
currentMultiViewModel = BinaryMultiViewModel.OneViewModel;
}
public ViewModelBase CurrentMultiViewModel
{
get
{
return currentMultiViewModel;
}
set
{
if (currentMultiViewModel == value)
{
return;
}
currentMultiViewModel = value;
RaisePropertyChanged("CurrentMultiViewModel");
}
}
}
}
MultiView.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" Content="{Binding CurrentMultiViewModel}" />
</Grid>
OneViewModel.cs:
namespace Test.ViewModel
{
public class OneViewModel : ViewModelBase
{
public OneViewModel()
{
Messenger.Default.Register<Message>(this,"Token", FromMultiModel);
}
private void FromMultiModel(Message input)
{
MessageBox.Show(input.LoadingIndication);
}
}
}
OneView.cs
namespace Test.Views
{
public partial class OneView : UserControl
{
public OneView()
{
DataContext = new OneViewModel();
InitializeComponent();
}
}
}
App.xaml:
<DataTemplate DataType="{x:Type vm:OneViewModel}">
<views:OneView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:FourViewModel}">
<views:FourView/>
</DataTemplate>
You cannot create OneViewModel in OneView's ctor and assing it to datacontext.
The OneViewModel instance is created in BinaryMultiViewModel and when you set it to ContentControl using databinding and DataTempate with OneView usercontrol is chosen, then OneViewModel is set automatically as datacontext of OneView
just remove the line DataContext = new OneViewModel(); and it should work.
Related
I'm learning creation of menu. I have some issue, maybe the same that this post.
My app contains :
one master container (MainWindow)
one menu (MenuView)
some views
The MainWindow is defined like this (two columns, one for the menu, the other for views):
<Grid Background="{StaticResource PrimaryBackgroundColor}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" Content="{Binding Menu}"/>
<ContentControl Grid.Column="1" Content="{Binding SelectedViewModel}"/>
</Grid>
When we click on menu items, there are no displayed views (built with UserControl).
I add the following codes :
MainWindowViewModel.cs
#region Constructor
public MainWindowViewModel()
{
Menu = new MenuViewModel();
}
#endregion Constructor
#region Properties
private object _menu;
public object Menu
{
get
{
return _menu;
}
set
{
_menu = value;
OnPropertyChanged(nameof(Menu));
}
}
private object _selectedViewModel;
public object SelectedViewModel
{
get
{
return _selectedViewModel;
}
set
{
_selectedViewModel = value;
OnPropertyChanged(nameof(SelectedViewModel));
}
}
#endregion Properties
MenuViewModel.cs
#region Variable
MainWindowViewModel mainWindowObj;
#endregion Variable
#region Constructor
public MenuViewModel()
{
menuCommand = new RelayCommand(load_menuChoiced);
}
#endregion Constructor
#region Properties
public ICommand menuCommand { get; set; }
#endregion Properties
#region Public Methods
#endregion Public Method
#region Private Method
public void load_menuChoiced(object obj)
{
switch (obj)
{
case "Home":
mainWindowObj = new MainWindowViewModel()
{
SelectedViewModel = new HomeViewModel()
};
break;
case "Graphic":
break;
case "Setting":
break;
default:
break;
}
}
#endregion Private Method
Could you explain me why it doesn't work and help me to fix it ?
Thanks a lot
It may be that HomeView was not found, Add this code to MainWindow's Resources property.
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<view:HomeView/>
</DataTemplate>
This is my first ever post, so bear with me.
I am using Xamarin Forms.
I am trying to update a shopping cart style ObservableCollection. My XAML file has a button that executes the command "AddCommand" in the below code. I know this works because the Basket ObservableCollection updates (Checked with DisplayAlert after getting object). I know that the OnPropertyChanged() gets called as I have put a breakpoint there and it flags.
My Issue is that the UI does not update after this addition. If I add objects to begin with these load fine, only the objects added using the AddToBasket function fail to show.
BasketViewModel.cs:
namespace SacredGrounds.ViewModel
{
public class BasketViewModel : ViewModelBase
{
ObservableCollection<BasketItem> _Basket;
public ObservableCollection<BasketItem> Basket
{
get { return _Basket; }
set
{
_Basket = value;
OnPropertyChanged();
}
}
public BasketViewModel()
{
Basket = new ObservableCollection<BasketItem>();
}
public ICommand AddCommand => new Command(AddToBasket);
private async void AddToBasket()
{
Basket.Add(new BasketItem { BasketItemType = BasketItemType.Drink, ProductName = "Test", ProductImage = "coffeeBeans.png", UnitPrice = 2, Quantity = 1 });
await NavigationService.Instance.SwitchToDrinks();
}
}
ViewModelBase.cs
using System.Threading.Tasks;
using Xamarin.Forms;
namespace SacredGrounds.ViewModel.Base
{
public class ViewModelBase : BindableObject
{
public virtual Task InitializeAsync(object navigationData) => Task.FromResult(false);
}
}
Relevant part of CartPopupView.Xaml
</pancake:PancakeView>
<Grid
Grid.Row="1"
BackgroundColor="{StaticResource GreenColor}">
<Grid.RowDefinitions>
<RowDefinition Height="250" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- BASKET ITEMS -->
<ListView
x:Name="listView1"
Grid.Row="0"
ItemsSource="{Binding Basket}"
ItemTemplate="{StaticResource BasketItemDataTemplateSelector}"
HasUnevenRows="True"
SeparatorVisibility="Default"
SelectionMode="None"
ItemTapped="RemoveItem"
Margin="0, 6"/>
Relevant part of CartPopupView.Xaml.cs
namespace SacredGrounds.Views
{
public partial class CartPopupView : ContentView
{
public delegate void TapDelegate();
public CartPopupView ()
{
InitializeComponent ();
GoToState("Collapsed");
}
Could someone please advise me on why my UI is not updating from this code?
EDIT:
Thanks to Jason for the help in realizing my error.
I was creating a new instance of the class BasketViewModel for each page, thus the items in the Observable Collection were obviously different depending on which instance I was accessing. The way I handled this was to add a Static Instance which I passed between pages.
BasketViewModel.cs
namespace SacredGrounds.ViewModel
{
public class BasketViewModel : ViewModelBase
{
public static BasketViewModel _instance;
public static BasketViewModel Instance
{
get
{
if (_instance == null)
_instance = new BasketViewModel();
return _instance;
}
}
ObservableCollection<BasketItem> _Basket;
public ObservableCollection<BasketItem> Basket
{
get { return _Basket; }
set
{
_Basket = value;
OnPropertyChanged();
}
}
public BasketViewModel()
{
Basket = new ObservableCollection<BasketItem>();
}
I just started learning MVVM and here is what seems to be basic question but I spent whole day trying to figure it out.
I have a solution that contains 3 projects one for Model, one for ViewModel and one for View. The Model contains a class that has 2 properties Text and CheckStatus.
The ViewModel has a list called listOfItems that has three items, each item has these 2 properties from the Model.
The View has a listView inside it there is a CheckBox. What is the proper way to bind the CheckBox content to the property Text?
Here is the model
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TheModel
{
public class CheckBoxListModel : INotifyPropertyChanged
{
private string text;
public string Text
{
get { return text; }
set
{
text = value;
RaiseChanged("Text");
}
}
private bool checkStatus;
public bool CheckStatus
{
get { return checkStatus; }
set
{
checkStatus = value;
RaiseChanged("CheckStatus");
}
}
private void RaiseChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Here is the view model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using TheModel;
namespace TheViewModel
{
public class TheViewModel
{
public List<CheckBoxListModel> ListOfItems { get; set; }
public TheViewModelClass()
{
ListOfItems = new List<CheckBoxListModel>
{
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 1",
},
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 2",
},
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 3",
}
};
}
public static implicit operator List<object>(TheViewModelClass v)
{
throw new NotImplementedException();
}
}
}
and here is the View XAML
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:TheView.Managers" xmlns:TheViewModel="clr-
namespace:TheViewModel;assembly=TheViewModel"
x:Class="TheView.Styles.ListViewDatabaseStyle">
<UserControl.DataContext>
<TheViewModel:TheViewModelClass/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Button Content="Continue" Style="{StaticResource ButtonStyle}"
Margin="1104,27,40,40"/>
<ListView x:Name="listView1" SelectionMode="Multiple"
Style="{StaticResource ListViewStyle}" Margin="10,55,10,10"
ctrl:ListViewLayoutManager.Enabled="true" ItemsSource="
{Binding TheViewModelClass}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Competency Items"
ctrl:ProportionalColumn.Width="1100"/>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle >
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding
CheckedStatus}"/>
<Setter Property="HorizontalContentAlignment"
Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox
Click="CheckBox_Click"
Content="{Binding Path=TheViewModelClass.Text}"
IsChecked="{Binding
Path=TheViewModelClass.CheckedStatus}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
Here is the View behind code, I know I shouldn't have something here but where should that part go?
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System;
using System.Text;
using TheViewModel;
namespace TheView.Styles
{
public partial class ListViewDatabaseStyle : UserControl
{
public ListViewDatabaseStyle()
{
InitializeComponent();
}
public List<string> selectedNames = new List<string>();
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var ChkBox = sender as CheckBox;
var item = ChkBox.Content;
bool isChecked = ChkBox.IsChecked.HasValue ? ChkBox.IsChecked.Value
: false;
if (isChecked)
selectedNames.Add(item.ToString());
else
selectedNames.Remove(item.ToString());
}
}
}
This is all quite ridiculous.
Here is a much easier way which involves no external libraries, no additional housekeeping classes and interfaces, almost no magic, and is very flexible because you can have viewmodels that contain other viewmodels, and you get to instantiate each one of them, so you can pass constructor parameters to them:
For the viewmodel of the main window:
using Wpf = System.Windows;
public partial class TestApp : Wpf.Application
{
protected override void OnStartup( Wpf.StartupEventArgs e )
{
base.OnStartup( e );
MainWindow = new MainView();
MainWindow.DataContext = new MainViewModel( e.Args );
MainWindow.Show();
}
}
For all other viewmodels:
This is in MainViewModel.cs:
using Collections = System.Collections.Generic;
public class MainViewModel
{
public SomeViewModel SomeViewModel { get; }
public OtherViewModel OtherViewModel { get; }
public Collections.IReadOnlyList<string> Arguments { get; }
public MainViewModel( Collections.IReadOnlyList<string> arguments )
{
Arguments = arguments;
SomeViewModel = new SomeViewModel( this );
OtherViewModel = new OtherViewModel( this );
}
}
This in MainView.xaml:
[...]
xmlns:local="clr-namespace:the-namespace-of-my-wpf-stuff"
[...]
<local:SomeView DataContext="{Binding SomeViewModel}" />
<local:OtherView DataContext="{Binding OtherViewModel}" />
[...]
As you can see, a viewmodel can simply be a member (child) of another viewmodel; in this case SomeViewModel and OtherViewModel are children of MainViewModel. Then, in the XAML file of MainView, you can just instantiate each of the child views and specify their DataContext by Binding to the corresponding child viewmodels.
First of all. Set dependencies of projects. ViewModel must have access Model. (View and Model projects do not have to reference to other projects.) If i were you i would make a StartUp Project to transfer the control to ViewModel.
This "StartUp" project should be WPF, all of others should be "class library" but don't forget to add the required references to projects (For example the system.xaml for your view project to create usercontrols.)
Projects dependencies:
- StartUp --> ViewModel;
(- ViewModel --> View; or avoid this with DI)
- ViewModel --> Model;
(I should make another project for interfaces just this is just my perversions.)
StartUp Project:
Now in your startup (WPF) project should contains in (app.xaml.cs):
protected override void OnStartup(StartupEventArgs e)
{
// delete the startupuri tag from your app.xaml
base.OnStartup(e);
//this MainViewModel from your ViewModel project
MainWindow = new MainWindow(new MainViewModel());
}
The only one thing (Window) in your startup wpf project (to display your UserControls).
MainWindow.xaml content:
<Window x:Class="StartUp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" WindowState="Maximized" WindowStyle="None" AllowsTransparency="True">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding Control}"/>
</Window>
(and xaml.cs)
public partial class MainWindow : Window
{
public MainWindow(INotifyPropertyChanged ViewModel)
{
InitializeComponent();
this.DataContext = ViewModel;
this.Show();
}
}
And Thats all your StartUp WPF project.
In this way we gave the control to your ViewModel project.
(Okay, its just an extra, but i should make a "ViewService" to handle my UserControls)
Interface to find all of View and match the View with ViewModel.
public interface IControlView
{
INotifyPropertyChanged ViewModel { get; set; }
}
I created a singleton to store and match my views with my viewmodels. (You can skip this part.) I defined this in Model project.
public class ViewService<T> where T : IControlView
{
private readonly List<WeakReference> cache;
public delegate void ShowDelegate(T ResultView);
public event ShowDelegate Show;
public void ShowControl<Z>(INotifyPropertyChanged ViewModel)
{
if (Show != null)
Show(GetView<Z>(ViewModel));
}
#region Singleton
private static ViewService<T> instance;
public static ViewService<T> GetContainer
{
get
{
if (instance == null)
{
instance = new ViewService<T>();
}
return instance;
}
}
private ViewService()
{
cache = new List<WeakReference>();
var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(r => typeof(T).IsAssignableFrom(r) && !r.IsInterface && !r.IsAbstract && !r.IsEnum);
foreach (Type type in types)
{
cache.Add(new WeakReference((T)Activator.CreateInstance(type)));
}
}
#endregion
private T GetView<Z>(INotifyPropertyChanged ViewModel)
{
T target = default(T);
foreach (var wRef in cache)
{
if (wRef.IsAlive && wRef.Target.GetType().IsEquivalentTo(typeof(Z)))
{
target = (T)wRef.Target;
break;
}
}
if(target==null)
target = (T)Activator.CreateInstance(typeof(Z));
if(ViewModel != null)
target.ViewModel = ViewModel;
return target;
}
}
And now you have got a "service" to show your UserControls in the mainwindow from your
ViewModel:
public class MainViewModel : INotifyPropertyChanged
{
private IControlView _control;
public IControlView Control
{
get
{
return _control;
}
set
{
_control = value;
OnPropertyChanged();
}
}
public MainViewModel()
{ //Subscribe for the ViewService event:
ViewService<IControlView>.GetContainer.Show += ShowControl;
// in this way, here is how to set a user control to the window.
ViewService<IControlView>.GetContainer.ShowControl<ListViewDatabaseStyle>(new TheViewModel(yourDependencyItems));
//you can call this anywhere in your viewmodel project. For example inside a command too.
}
public void ShowControl(IControlView ControlView)
{
Control = ControlView;
}
//implement INotifyPropertyChanged...
protected void OnPropertyChanged([CallerMemberName] string name = "propertyName")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
If you don't want to use this "ViewService". Just create an UserControl instance, match DataContext of View with your ViewModel and give this view to Control property.
Here is your ViewModel with list (still in ViewMoldel project.)
public class TheViewModel
{
private readonly ObservableCollection<ISelectable> listOfItems;
public ObservableCollection<ISelectable> ListOfItems
{
get { return listOfItems; }
}
public ICommand SaveCheckedItemsText{
get{ return new RelayCommand(CollectNamesOfSelectedElements);}
}
public IEnumerable<ISelectable> GetSelectedElements
{
get { return listOfItems.Where(item=>item.CheckStatus); }
}
public TheViewModel(IList<ISelectable> dependencyItems)
{
listOfItems= new ObservableCollection<ISelectable>(dependencyItems);
}
//here is your list...
private List<string> selectedNames
//use this...
private void CollectNamesOfSelectedElements()
{
selectedNames = new List<string>();
foreach(ISelectable item in GetSelectedElements)
{
//you should override the ToString in your model if you want to do this...
selectedNames.Add(item.ToString());
}
}
}
RelayCommand article
View: (Keep here all of your usercontrols.)
In your UserControl (xaml):
<UserControl x:Class="View.ListViewDataStyle"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d">
<Button Command={Binding SaveCheckedItemsText}/>
<!-- Another content -->
<ListView ItemsSource="{Binding ListOfItems}">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" IsChecked="{Binding CheckedStatus}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
And with interface here is the xaml.cs code (for UserControls):
public partial class ListViewDatabaseStyle : UserControl, IControlView
{
public ListViewDatabaseStyle ()
{
InitializeComponent();
}
public INotifyPropertyChanged ViewModel
{
get
{
return (INotifyPropertyChanged)DataContext;
}
set
{
DataContext = value;
}
}
}
And the last one is the Model project with your models:
public interface ISelectable
{
bool CheckStatus { get; set; }
}
public class CheckBoxListModel : INotifyPropertyChanged, ISelectable
{
private string text;
public string Text
{
get { return text; }
set
{
text = value;
RaiseChanged("Text");
}
}
private bool checkStatus;
public bool CheckStatus
{
get { return checkStatus; }
set
{
checkStatus = value;
RaiseChanged("CheckStatus");
}
}
private void RaiseChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Excuse me for english grammar mistakes, i hope you understood my post.
Update:
Use the DI techn. to avoid the reference to view from viewmodel. DI service will inject the correct object with constructor injection.
<UserControl.DataContext>
<TheViewModel:TheViewModelClass/>
</UserControl.DataContext>
<ListView ItemsSource="{Binding ListOfItems}">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" IsChecked="{Binding CheckedStatus}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have MainWindow thath holds <ContentControl ... Content="{Binding CurrentViewModel}" in MainViewModel I am switching between two View FirstView,SecondView. FirstView contains usercontrol ContentFirstView that implementing async data sending to ContentSecondViewModel. Data send's with time delay 1000ms. The main question is why when i am clicking on 1,2,1,2,1,2 buttons the speed of updating label Count in ContentSecondView greatly increasing? I think that ContentSecondViewModel do not disposing and every time when i am clicking on button 2, it creates new object of Messenger.Register ...
MainModel.xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="1" Content="{Binding CurrentViewModel}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Button Content="1" Command="{Binding FirstCommand}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="2" Command="{Binding SecondCommand}" HorizontalAlignment="Left" Margin="10,78,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
MainViewModel.cs:
public class MainViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
///
public RelayCommand FirstCommand
{
get
{
return new RelayCommand(() => SwitchFirst());
}
}
public RelayCommand SecondCommand
{
get
{
return new RelayCommand(()=>SwitchSecond());
}
}
public static readonly FirstViewModel firstViewModel = new FirstViewModel();
public static readonly SecondViewModel secondViewModel = new SecondViewModel();
ViewModelBase currentViewModel;
public ViewModelBase CurrentViewModel
{
get
{
return currentViewModel;
}
set
{
if (currentViewModel == value)
return;
currentViewModel = value;
RaisePropertyChanged("CurrentViewModel");
}
}
public void SwitchFirst()
{
CurrentViewModel = MainViewModel.firstViewModel;
}
public void SwitchSecond()
{
CurrentViewModel = MainViewModel.secondViewModel;
}
public MainViewModel()
{
CurrentViewModel = MainViewModel.secondViewModel;
////if (IsInDesignMode)
////{
//// // Code runs in Blend --> create design time data.
////}
////else
////{
//// // Code runs "for real"
////}
}
}
SecondView.xaml:
<Grid>
<views:ContentSecondView></views:ContentSecondView>
</Grid>
FirstView.xaml:
<Grid>
<views:ContentFirstView></views:ContentFirstView>
</Grid>
ContentFirstView.xaml:
<Grid>
<Label>View That sending data</Label>
</Grid>
ContentSecondView.xaml:
<Grid>
<Label>View That sending data</Label>
</Grid>
ContentFirstViewModel.cs:
public class ContentFirstViewModel
{
public RelayCommand SendMessage
{
get
{
return new RelayCommand(() => Send());
}
}
public void Send()
{
Messenger.Default.Send<MessageCommuniactor>(new MessageCommuniactor {State=1 });
}
public void Increase()
{
while(true)
{
System.Threading.Thread.Sleep(1000);
Messenger.Default.Send<MessageCommuniactor>(new MessageCommuniactor { State = 1 });
}
}
public ContentFirstViewModel()
{
Action A = new Action(Increase);
IAsyncResult result = A.BeginInvoke(null, null);
}
}
ContentSecondViewModel.cs:
public class ContentSecondViewModel:ViewModelBase
{
private int count;
public int Count
{
get
{
return this.count;
}
set
{
this.count = value;
this.RaisePropertyChanged("Count");
}
}
public ContentSecondViewModel()
{
Messenger.Default.Register<MessageCommuniactor>(this, (key) =>
{
Count += key.State;
});
}
}
Your DataTemplate ensures that a new view is created every time a ContentFirstViewModel is present. Instead, go with a singleton approach where a singleton view is injected when needed. You can use Prism's regionmanager or simple binding for that.
1 - I have the TabControl. Its items source is collection of Tabs with
different types. I need to have a different XAML for each type. How to form TabItem header and content depends on ViewModel type?
2 - What is the best solution to encapsulate the XAML for each type of ViewModel? Should I have one UserControl for each type or there is a better solution?
HumanTabViewModel and InvaderTabViewModel are children of BaseViewModel class.
<TabControl ItemsSource="{Binding Tabs}">
</TabControl>
class PanelViewModel : BaseViewModel
{
private readonly ObservableCollection<BaseViewModel> _tabs = new ObservableCollection<BaseViewModel>();
public ObservableCollection<BaseViewModel> Tabs
{
get { return _tabs; }
}
private void InitTabs()
{
// Fill Tabs collection with some logic
var tab1 = new HumanTabViewModel ();
_tabs.Add(tab1);
var tab2 = new InvaderTabViewModel ();
_tabs.Add(tab2);
}
}
With the use of DataTemplates you can define a different looks for your types :
A DataTemplate is used to give a logical entity (.cs) a visual representation , once you assign your logical object (in your case invader/human vm's) as a Content , the framework will traverse up the logical tree looking for a DataTemplate for your type.
if it does not find any , it would just show the "ToString()" of your type.
In your case you have 2 Contents the TabItem.Content , and Header where can be assigned a DataTemplate via HeaderTemplate.
HumanView and InvaderView are UserControls.
CS :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Items.Add(new HumanViewModel());
Items.Add(new InvaderViewModel());
}
private ObservableCollection<BaseViewModel> items;
public ObservableCollection<BaseViewModel> Items
{
get
{
if (items == null)
items = new ObservableCollection<BaseViewModel>();
return items;
}
}
}
public class BaseViewModel : INotifyPropertyChanged
{
public virtual string Header
{
get { return "BaseViewModel"; }
}
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class HumanViewModel : BaseViewModel
{
public override string Header
{
get
{
return "HumanViewModel";
}
}
}
public class InvaderViewModel : BaseViewModel
{
public override string Header
{
get
{
return "InvaderViewModel";
}
}
}
XAML :
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:HumanViewModel}">
<local:HumanView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:InvaderViewModel}">
<local:InvaderView />
</DataTemplate>
<Style TargetType="TabItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Header,Mode=OneWay}" FontSize="18" FontWeight="Bold" Foreground="DarkBlue" Width="Auto"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding Items, Mode=OneWay}" />
</Grid>
</Window>