Как изменить контент UserControl на другой UserControl через модель основного вида. Как перемещаться между контентом - PullRequest
0 голосов
/ 24 февраля 2020

У меня есть главное окно с боковой панелью для навигации и пользовательский контроль, в котором я показываю 3 вида (по умолчанию, вид1, вид2). В модели основного представления (называемой AppVM) я инициализирую управление контентом в представлении по умолчанию, в котором есть кнопка для перехода к view1 (кроме боковой панели навигации). У меня есть команды в AppVM, чтобы перейти к любому из трех представлений. В этом случае View1 имеет еще одну кнопку, которая должна перейти в view2 (используя команду, присутствующую в модели основного вида). Однако всякий раз, когда я нажимаю кнопку в view1 (чтобы перейти к view2), дисплей не меняется. Особенностью является то, что при отладке при нажатии кнопки в view1 переменная, к которой привязан элемент управления контентом, устанавливается в представление по умолчанию, а не в текущее представление view1.

Я думаю, что, как я настроил команду, она создает новый экземпляр переменной управления содержимым, но я не могу понять, как заставить его использовать один и тот же экземпляр и не открывать новые экземпляры снова и снова ,

Модель основного вида (AppVM)

  public class AppVM : ObservableObject
    {

        //Create a property that controls current view
        private object _currentView;
        public object CurrentView
        {
            get { return _currentView; }
            private set
            {
                OnPropertyChanged(ref _currentView, value);
            }
        }

        private string _textboxText;

        public string TextboxText
        {
            get { return _textboxText; }
            set
            {
                OnPropertyChanged(ref _textboxText, value);
            }
        }


        //Instantiate the relaycommands, we will need to instantiate relaycommand objects for every command we need to perform. 
        //This means that we will need to do this for preses of all buttons
        public RelayCommand View1ButtonCommand { get; private set; }
        public RelayCommand View2ButtonCommand { get; private set; }

        public RelayCommand DefaultCommand { get; private set; }



        public AppVM()
        {

            //CurrentView = this;
            CurrentView = new DefaultVM();
            View1ButtonCommand = new RelayCommand(ShowView1, AlwaysTrueCommand);
            View2ButtonCommand = new RelayCommand(ShowView2, AlwaysTrueCommand);
            DefaultCommand = new RelayCommand(ShowDefault, AlwaysTrueCommand);


        }

        public void ShowDefault(object dummy)
        {
          //  CurrentView = null;
            CurrentView = new DefaultVM();

        }

        public void ShowView1(object dummy)
        {
            //CurrentView = null;
            CurrentView =  new View1(dummy as string);

        }

        public void ShowView2(object dummy)
        {
            // CurrentView = null;
            CurrentView =  new View2();
        }


        public bool AlwaysTrueCommand(object dummy)
        {
            return true;
        }       
    }

View1 VM

public class View1VM : ObservableObject     {

        public InfoClass View1InfoClass { get; set; }



        public View1VM()        {           View1InfoClass = new InfoClass //Apparently I  need to instantiate and initialize this to activate binding          {

                FirstName =  "Abbas",
                //FirstName = passedInforClass,
                LastName = "Syed",
                Number = 12

            };


        }   }

Команда в view1.xaml

<UserControl.Resources>
        <vm:AppVM x:Name="AppVMinView1" x:Key="AppVMinView1"></vm:AppVM>
    </UserControl.Resources>
    <UserControl.DataContext>
        <vm:View1VM></vm:View1VM>
    </UserControl.DataContext>
    <Grid Background="Aqua">
        <StackPanel Margin="100">
            <TextBlock Text="First Name"/>
            <TextBox x:Name="firstNameTextBoxView1" Text="{Binding View1InfoClass.FirstName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
            <TextBlock Text="Last Name"/>
            <TextBox x:Name="lastNameTextBoxView1" Text="{Binding View1InfoClass.LastName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
            <TextBlock Text="Random Useless Number" ></TextBlock>
            <TextBox x:Name="randomUselessNumberView1" Text="{Binding View1InfoClass.Number, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>

            <TextBlock Text="First Name Entered"></TextBlock>
            <TextBlock Text="{Binding View1InfoClass.FirstName}"></TextBlock>
            <TextBlock Text="Last Name Entered" ></TextBlock>
            <TextBlock Text="{Binding View1InfoClass.LastName}"></TextBlock>
            <TextBlock Text="Random Useless Number Entered"></TextBlock>
            <TextBlock Text="{Binding View1InfoClass.Number}"></TextBlock>

            <Button DataContext="{DynamicResource AppVMinView1}" Content="Go to view2" Height="20" Width="70" Command="{Binding View2ButtonCommand}" />



        </StackPanel>
    </Grid>

Из того, что у меня есть прочитайте (здесь и в inte rnet), мне нужно сделать представления одноэлементными, я попытался сделать это, объявив свойство stati c для view2, инициализированного для нового view2 с частным установщиком, но это не разрезал это. Буду благодарен за любую помощь в этом.

Я должен также добавить, что даже когда кнопка в view1 для перехода в view2 не работает, кнопки боковой навигационной панели работают просто отлично.

1 Ответ

0 голосов
/ 25 февраля 2020

Похоже, вы создаете несколько экземпляров AppVm.
Кнопка в View1 и кнопки панели навигации, очевидно, не привязываются к одному и тому же экземпляру AppVm.
То же применяется к свойству CurrentView: ваш хост контента связывается с другим экземпляром, т. е. ссылками на значения свойства, отличными от CurrentView, который вы изменяете в View1. Поэтому изменение CurrentView изнутри View1 не влияет на хост содержимого -> представление никогда не меняется.
Убедитесь, что вы всегда ссылаетесь на один и тот же (общий) экземпляр в одном и том же контексте.

Существует несколько способов достижения этого в зависимости от структуры вашего пользовательского интерфейса. Создание Singleton класса модели представления - безусловно, худший выбор. Синглетонов следует и всегда можно избежать.

Самое простое решение - объявить модель представления в качестве ресурса в приложении, xaml ResourceDictionary:

App.xaml
Ресурсы, определенные в этом файле, доступны глобально в любом контексте XAML с помощью поиска по словарю расширения разметки StaticResource.

<Application>
  <Application.Resources>
    <AppVM x:Key="AppVMinView1" />
  </Application.Resources>
</Application>

Внутри любого файла XAML (область приложения):

<UserControl>
  <UserControl.DataContext>
    <View1VM />
  </UserControl.DataContext>

  <!-- Reference resources defined in App.xaml, using the StaticResource markup extension -->
  <Button Command="{Binding Source={StaticResource AppVMinView1}, Path=View2ButtonCommand}" />
</UserControl>

Рекомендованное решение

Также похоже, что вы создаете экземпляры ваших представлений или страниц внутри вашей модели представления (я предполагаю, что new View1(dummy as string) создает элемент управления, поскольку модель представления называется View1VM). Работа только с моделями представлений вместо этого также решает вашу проблему более элегантным способом.

Очень важно использовать один экземпляр моделей просмотра страниц, если вы не хотите терять состояние (и данные) при переключении представлений. (Не путайте это с Singleton, который является шаблоном проектирования, который гарантирует, что один экземпляр используется глобально , назначая его свойству static. Singleton Pattern обычно считается анти-шаблоном .)

Это короткий, но полный пример того, как показывать страницы и перемещаться по ним:

AppVM.cs

// Main view model
class AppVM : ObservableObject     
{
  // Create a property that controls current view
  private ObservableObject _currentView;
  public ObservableObject CurrentView
  {
    get => _currentView; 
    private set => OnPropertyChanged(ref _currentView, value);
  }

  private Dictionary<string, ObservableObject> Pages { get; set; }

  public AppVM()
  {
    // Create and store the pages, 
    // so that the same instances can be reused. 
    // All pages must extend ObservableObject (or any other common base type).
    this.Pages = new Dictionary<string, ObservableObject>()
    {
      { nameof(DefaultVM), new DefaultVM() },
      { nameof(View1VM), new View1VM() },
      { nameof(View2VM), new View2VM() },
    };    

    // Initialize first page
    this.CurrentView = this.Pages[nameof(DefaultVM)];

    this.DefaultCommand  = new RelayCommand(param => this.CurrentView = this.Pages[nameof(DefaultVM)], param => true);
    this.View1ButtonCommand = new RelayCommand(param => this.CurrentView = this.Pages[nameof(View1VM)], param => true);
    this.View2ButtonCommand = new RelayCommand(param => this.CurrentView = this.Pages[nameof(View2VM)], param => true);
  }  
}

View1.xaml

<!-- DataContext is inherited from the surrounding DataTemplate and is the corresponding page view model -->
<UserControl>    
  <StackPanel>
    <TextBox Text="{Binding View1InfoClass.FirstName}" />

    <!-- 
      Bind to the command of the same view model instance,
      which is the DataContext of the content host 
    -->
    <Button Content="Show View2"
            Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=MainWindow}, Path=DataContext.View2ButtonCommand}" />
  </StackPanel>
</UserControl>

MainWindow.xaml

<Window>
  <Window.DataContext>
    <AppVM />
  </Window.DataContext>
  <Window.Resources>

    <!-- Define the views as implicit (keyless) DataTemplate -->
    <DataTemplate DataType="{x:Type DefaultVM}">
      <DefaultView />
    </DataTemplate>

    <DataTemplate DataType="{x:Type View1VM}">
      <View1 />
    </DataTemplate>

    <DataTemplate DataType="{x:Type View2VM}">
      <View2 />
    </DataTemplate>
  </Window.Resources>

  <!-- 
    Host of the pages.
    The implicit DataTemplates will apply automatically 
    and show the control that maps to the current CurrentView view model
  -->
  <ContentPresenter Content="{Binding CurrentView}" />
</Window>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...