简体   繁体   中英

(WPF/MVVM) Single Instance In MainViewModel

I have a project that has 3 View/ViewModels (Until now of course!). So I have a ListView in MainView to show each one of them, and could be selected by the user. So I used something like this:

class MainViewModel :INotifyPropertyChanged
{
    public ObservableCollection<BaseViewModel> obv
    {
        get { return this._obv; }
    }
    public MainViewModel()
    {
        pvm = new PViewModel();
        lvm = new LViewModel();
        svm = new SViewModel();
        cvm = new CViewModel();
        ivm = InstanceViewModel.Instance;
        obv.Add(pvm);
        obv.Add(lvm);
        obv.Add(svm);
    }
    .
    .
    .
}

And this is the MainView:

<Window x:Class="Banking_System.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Banking_System"
    xmlns:view="clr-namespace:Banking_System.View"
    xmlns:model="clr-namespace:Banking_System.Model"
    xmlns:viewmodel="clr-namespace:Banking_System.ViewModel"
    mc:Ignorable="d"
    Title="MainWindow" Height="550" Width="1200" MinHeight="500" MinWidth="800">
<Window.DataContext>
    <viewmodel:MainViewModel/>
</Window.DataContext>
<Window.Resources>
    <BooleanToVisibilityConverter x:Key="boo2vis"/>
    <DataTemplate DataType="{x:Type viewmodel:PViewModel}">
        <view:P/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewmodel:LViewModel}">
        <view:L/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewmodel:SViewModel}">
        <view:S/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewmodel:CViewModel}">
        <view:C/>
    </DataTemplate>
</Window.Resources>
<Grid FlowDirection="RightToLeft">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="30*"/>
        <ColumnDefinition Width="100*"/>
    </Grid.ColumnDefinitions>
    <ListBox x:Name="ListBoxMenu" Margin="5" Grid.Column="0" ItemsSource="{Binding obv}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}" Margin="10" Padding="10"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <Border Grid.Column="1" Margin="5" BorderBrush="#FF7F9DB9" BorderThickness="1">
        <ContentControl Margin="10" Content="{Binding SelectedItem, ElementName=ListBoxMenu}"/>
    </Border>
</Grid>

And then Bind the ListView to obv . But after doing this, When SelectedItem in the ListView changes, This MainViewModel gets a new instance And because of that all the other ViewModels in it, will get a new instance.(Because I wrote new *Viewmodel() in it!)
(In another word, all the application get refresh, Like its just got opened!)

Q: So is there anyway to prevent this kind of action?
Note: Although i could use Instances in all the ViewModels and write something like this:

/* In each ViewModel have something like this */
public static LViewModel Instance
    {
        get
        {
            if (_Instance == null)
                _Instance = new LViewModel();
            return _Instance;
        }      
    }


/* Then change the MainViewModel to something like this */

class MainViewModel :INotifyPropertyChanged
{
    public ObservableCollection<BaseViewModel> obv
    {
        get { return this._obv; }
    }
    public MainViewModel()
    {
        pvm = PViewModel.Instance;
        lvm = LViewModel.Instance;
        svm = SViewModel.Instance;
        cvm = CViewModel.Instance;
        ivm = InstanceViewModel.Instance;
        obv.Add(pvm);
        obv.Add(lvm);
        obv.Add(svm);
    }
    .
    .
    .
}

But I don't if it's right or not!

Update: I don't know about Locator at all! So add another class name Locator with this code:

class Locator
{
    public LViewModel LInstance
    {
        get { return new LViewModel(); }
    }
    public PViewModel PInstance
    {
        get { return new PViewModel(); }
    }
    public SViewModel SInstance
    {
        get { return new SViewModel(); }
    }
    public MainViewModel MainInstance
    {
        get { return new MainViewModel(); }
    }
}

But my MainViewModel still as the same as before! I did change my xaml to this:

<!--<Window.DataContext>
    <viewmodel:MainViewModel/>
</Window.DataContext>-->
.
.
.
<!-- Just added the DataContext -->
<ListBox x:Name="ListBoxMenu" Margin="5" Grid.Column="0" ItemsSource="{Binding obv}" DataContext="{Binding MainInstance, Source={StaticResource LocatorVM}}">
.
.
.

But still like before, MainViewModel and there for other ViewModels got reloaded every time!

Yes, you'll need to either create a "singleton" of your VM's, or you can use a Locator like the one that most MVVM frameworks supply. (which basically does the same really...).

Let's see if I can link you ...

Well, my my ... what do you know. have a look at this answer for more about the locator

Almost. You want something like:

class Locator
{
    private LViewModel _lInstance;
    public LViewModel LInstance
    {
        get { return _lInstance ?? (_lInstance = new LInstance()); }
    }
    ....
}

This way, you're creating your instance once, and only when requested, and they should survive between changes :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM