Как я могу переключаться между представлениями из другого потока? - PullRequest
0 голосов
/ 20 сентября 2011

Я пытаюсь использовать MVVM, поэтому у меня есть:

MainWindow:

<Window x:Class="tbtest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TabControl ItemsSource="{Binding Tabs}" IsSynchronizedWithCurrentItem="True" Grid.Row="0"/>
        <Button Command="{Binding AddNewTab}" Grid.Row="1">Add new tab</Button>
    </Grid>
</Window>

И ViewModel:

public class VM
{
    public VM()
    {
        Tabs = new ObservableCollection<Object>();
        AddNewTab = new DelegateCommand<object>(ExecAddNewTab);
    }

    private void ExecAddNewTab(object obj)
    {
        var tab = new SomeTab();
        Tabs.Add(tab.View);
        ThreadPool.QueueUserWorkItem(item => tab.Activate());
    }

    public ObservableCollection<Object> Tabs { get; private set; }
    public DelegateCommand<object> AddNewTab { get; private set; }
}

Каждая вкладка является отдельной ViewModel с View:

public class SomeTab : ITab, INotifyPropertyChanged
{
    public SomeTab()
    {
        View = new SomeTabView1 {DataContext = this};
    }

    public string Header { get { return "Header"; } }
    private object _view;
    public object View
    {
        get { return _view; }
        private set
        {
            _view = value;
            PropertyChanged(this, new PropertyChangedEventArgs("View"));
        }
    }

    public void Activate()
    {
        Thread.Sleep(2000);

        // Some work

        Application.Current.Dispatcher.BeginInvoke(new Action<SomeTab>(vm =>
                                                                   {
// switching views, how can it be done?
                                                                          vm.View = new SomeTabView2 { DataContext = this };

                                                                   }), this);
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
}

SomeTabView1 И SomeTabView2 - это простой пользовательский контроль WPF с TextBlock 'View1' и 'View2'.

Мне нужен метод Activate (запущенный в отдельном потоке), переключающий View1 на View2.
Этот код, который я предоставил, не работает.

Что я могу сделать, чтобы получить желаемое? Спасибо.

Ответы [ 2 ]

1 голос
/ 20 сентября 2011

Ваши ViewModels не должны работать с Views

Вместо этого, чтобы ваше MainViewModel содержало свойство SelectedTab и связывало его с вашим TabControl, затем для переключения вкладок вы устанавливаете только SelectedTab. Используйте DataTemplates, чтобы сообщить WPF, как рисовать каждую модель представления

MainWindow XAML

<Window x:Class="tbtest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:local=clr-namespace:MyNamespace>

    <Window.Resources>
        <DataTemplate DataType="{x:Type local:SomeTabViewModel1}">
            <local:SomeTabView1 /> 
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:SomeTabViewModel2}">
            <local:SomeTabView2 /> 
        </DataTemplate>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:SomeTabViewModel3}">
            <local:SomeTabView3 /> 
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TabControl ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}"
                    IsSynchronizedWithCurrentItem="True" Grid.Row="0"/>
        <Button Command="{Binding AddNewTab}" Grid.Row="1">Add new tab</Button>
    </Grid>
</Window>

MainWindow ViewModel

public class VM
{
    public VM()
    {
        Tabs = new ObservableCollection<Object>();
        AddNewTab = new DelegateCommand<object>(ExecAddNewTab);
    }

    private void ExecAddNewTab(object obj)
    {
        var tab = new SomeTab();
        Tabs.Add(tab);
        SelectedTab = tab;

        // This should only run whatever code is needed to initialize the
        // ViewModel. It should have nothing to do with views
        ((ITab)tab).Activate();
    }

    public object SelectedTab { get; private set; }
    public ObservableCollection<Object> Tabs { get; private set; }
    public DelegateCommand<object> AddNewTab { get; private set; }
}
0 голосов
/ 20 сентября 2011

Вышеприведенная проблема связана с тем, что ваш код "предполагает", что он выберет \ фокусирует только что созданный элемент Tab, просто используя IsSynchronizedWithCurrentItem.

. Необходимо заполнить это полена CollectionView.Также вы должны добавить новую вкладку, используя сам этот CollectionView.

Например

Предположим, у вас есть ListCollectionView с именем TabsView с исходной коллекцией как Tabs наблюдаемой коллекцией ...

Свяжите TabControl.ItemsSource с TabsView и затем вместо ...

   var tab = new SomeTab();
   Tabs.Add(tab.View); 

Используйте это ...

   var tabUi = this.TabsView.AddNew() as SomeTab;
   this.TabsView.CommitNew();
   tabUi.Activate();

Дайте мне знать, если это поможет....

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...