MaterialDesignInXAML / MaterialDesignInXamlToolkit

Google's Material Design in XAML & WPF, for C# & VB.Net.
http://materialdesigninxaml.net
MIT License
15.05k stars 3.41k forks source link

Implementation of transitions through MVVM #2346

Closed hugonavy closed 3 years ago

hugonavy commented 3 years ago

Hi!

I can't understand how to use transitions from ViewModel (MVVM) to set UserControls from C#. Is it possible to give a simple example?

StefanoRivolta-Previero commented 3 years ago

Do you mean you want to change the content of the slides based on some code in the ViewModel? You could do it like this:

  <UserControl.Resources>
    <DataTemplate DataType="{x:Type local:PageOneViewModel }">
      <local:PageOneView/>
    </DataTemplate>

    <DataTemplate DataType="{x:Type local:PageTwoViewModel }">
      <local:PageTwoView/>
    </DataTemplate>

    <DataTemplate DataType="{x:Type local:PageThreeViewModel }">
      <local:PageThreeView/>
    </DataTemplate>
  </UserControl.Resources>

  <Grid>

      <materialDesign:Transitioner SelectedIndex="{Binding SelectedIndex}" AutoApplyTransitionOrigins="True" >

        <materialDesign:TransitionerSlide OpeningEffect="{materialDesign:TransitionEffect FadeIn}" 
                                          Content="{Binding SlideOne}" />

        <materialDesign:TransitionerSlide Content="{Binding SlideTwo}">
          <materialDesign:TransitionerSlide.BackwardWipe>
            <materialDesign:FadeWipe />
          </materialDesign:TransitionerSlide.BackwardWipe>
          <materialDesign:TransitionerSlide.ForwardWipe>
            <materialDesign:CircleWipe/>
          </materialDesign:TransitionerSlide.ForwardWipe>
        </materialDesign:TransitionerSlide>
      </materialDesign:Transitioner>

  </Grid>
  public class MainViewModel : BaseViewModel
  {
    private BaseViewModel _slideOne;

    public BaseViewModel SlideOne
    {
      get { return _slideOne; }
      set { SetProperty(ref _slideOne, value); }
    }

    private BaseViewModel _slideTwo;

    public BaseViewModel SlideTwo
    {
      get { return _slideTwo; }
      set { SetProperty(ref _slideTwo, value); }
    }

    private int _selectedIndex;

    public int SelectedIndex
    {
      get { return _selectedIndex; }
      set { SetProperty(ref _selectedIndex, value); }
    }

    public MainViewModel()
    {
      SlideOne = new PageOneViewModel();
      SlideTwo = new PageTwoViewModel();

      SelectedIndex = 0;
    }

    public void ChangeSlideTwo()
    {
      SlideTwo = new PageThreeViewModel();
      SelectedIndex = 1;
    }

  }

Essentially you define the Views that are associated to the ViewModels, so WPF grabs that view when you set the content as a specific ViewModel (so you don't have to manage views in your ViewModels). Then you bind to the content of the slides and change that property to whichever ViewModels you need. You can bind to SelectedIndex to move the slides back and forth.

btw, I haven't tested the code.

hugonavy commented 3 years ago

Cool! I checked and it works! But can I set TransitionEffect from C#? In the example you showed, I have to explicitly set all the UserControls in XAML. Can I use binding to just one variable to manage transitions from C#?

For example, for DialogHost and dialog boxes, I do this

    private const string DialogHostId = "SomeDialogHostId";
    public UserControl DialogControl;

    public async void ShowSomeDialog()
    {
        DialogControl = new SomeUC { DataContext = SomeVM };
        await DialogHost.Show(DialogControl, DialogHostId);
        await Task.Delay(TimeSpan.FromSeconds(2));
    }
StefanoRivolta-Previero commented 3 years ago

But can I set TransitionEffect from C#?

Never tried, but technically you should be able to Bind to the OpeningEffect property and then define the TransitionEffect in the ViewModel.

TransitionEffect = new TransitionEffect(TransitionEffectKind.FadeIn);

In the example you showed, I have to explicitly set all the UserControls in XAML. Can I use binding to just one variable to manage transitions from C#?

Sure, but in theory it breaks the MVVM model since you manage Views in the ViewModel (not a huge deal really, just know it). Anyway just use the View instead of the ViewModel as the content. For instance:

  public class MainViewModel : BaseViewModel
  {
    private UserControl _slideOne;

    public UserControl SlideOne
    {
      get { return _slideOne; }
      set { SetProperty(ref _slideOne, value); }
    }

    private UserControl _slideTwo;

    public UserControl SlideTwo
    {
      get { return _slideTwo; }
      set { SetProperty(ref _slideTwo, value); }
    }

    private int _selectedIndex;

    public int SelectedIndex
    {
      get { return _selectedIndex; }
      set { SetProperty(ref _selectedIndex, value); }
    }

    public MainViewModel()
    {
      SlideOne = new PageOneView(){DataContext = new PageOneViewModel()};
      SlideTwo = new PageTwoView(){DataContext = new PageTwoViewModel()};

      SelectedIndex = 0;
    }

    public void ChangeSlideTwo()
    {
      SlideTwo = new PageThreeView(){DataContext = new PageThreeViewModel()};
      SelectedIndex = 1;
    }
  }

Remove the DataTemplates since you don't use them anymore

Keboo commented 3 years ago

@hugonavy If it helps I have several small Transitioner samples here that use MVVM. Specifically the DataBound, or DataDrivenSlides might be interesting.

Erapchu commented 3 years ago

Hello to all! Please, check this repo: https://github.com/Erapchu/TransitionsExample/tree/develop

hugonavy commented 3 years ago

Андрей, спасибо! :) На основе представленных выше вариантов я уже примерно к такой же реализации и пришел))