New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HamburgerMenu Command MVVM #2848

Closed
matej89 opened this Issue Feb 10, 2017 · 19 comments

Comments

Projects
None yet
6 participants
@matej89

matej89 commented Feb 10, 2017

Hi,

I use hamburger menu and now I have the problem how to set viewModel to ItemsSource.

My item source are:

<controls:HamburgerMenuGlyphItem Glyph="*" Label="test">
            <controls:HamburgerMenuGlyphItem.Tag>
                    <Views:ViewCaseReviewDebt/>
                       **There I want set Command (like on Button Command="{Binding testClick}")**
             </controls:HamburgerMenuGlyphItem.Tag>
  </controls:HamburgerMenuGlyphItem>

Thank you.

Regards,
Matej

@punker76

This comment has been minimized.

Show comment
Hide comment
@punker76

punker76 Feb 10, 2017

Member

@matej89 there are 2 options for you to set the selected content on the right side for the HamburgerMenu

  1. use the ItemClick and OptionsItemClick events
<controls:HamburgerMenu x:Name="HamburgerMenuControl"
						...
                        ItemClick="HamburgerMenuControl_OnItemClick"
                        OptionsItemClick="HamburgerMenuControl_OnItemClick"
                        ...
                        DisplayMode="CompactInline">
</controls:HamburgerMenu>

and

private void HamburgerMenuControl_OnItemClick(object sender, ItemClickEventArgs e)
{
    this.HamburgerMenuControl.Content = e.ClickedItem;
    this.HamburgerMenuControl.IsPaneOpen = false;
}
  1. with binding and a converter
<controls:HamburgerMenu x:Name="HamburgerMenuControl"
                        Foreground="White"
                        PaneBackground="#FF444444"
                        IsPaneOpen="False"
                        ItemTemplate="{StaticResource MenuItemTemplate}"
                        OptionsItemTemplate="{StaticResource OptionsMenuItemTemplate}"
                        DisplayMode="CompactInline">

    <controls:HamburgerMenu.Content>
        <MultiBinding Converter="{StaticResource SelectedItemToContentConverter}">
            <Binding RelativeSource="{RelativeSource Self}"
                     Mode="OneWay"
                     Path="SelectedItem" />
            <Binding RelativeSource="{RelativeSource Self}"
                     Mode="OneWay"
                     Path="SelectedOptionsItem" />
        </MultiBinding>
    </controls:HamburgerMenu.Content>
</controls:HamburgerMenu>

and

public class SelectedItemToContentConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        // first value is selected menu item, second value is selected option item
        if (values != null && values.Length > 1)
        {
            return values[0] ?? values[1];
        }
        return null;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return targetTypes.Select(t => Binding.DoNothing).ToArray();
    }
}

You can find some more infos here

Member

punker76 commented Feb 10, 2017

@matej89 there are 2 options for you to set the selected content on the right side for the HamburgerMenu

  1. use the ItemClick and OptionsItemClick events
<controls:HamburgerMenu x:Name="HamburgerMenuControl"
						...
                        ItemClick="HamburgerMenuControl_OnItemClick"
                        OptionsItemClick="HamburgerMenuControl_OnItemClick"
                        ...
                        DisplayMode="CompactInline">
</controls:HamburgerMenu>

and

private void HamburgerMenuControl_OnItemClick(object sender, ItemClickEventArgs e)
{
    this.HamburgerMenuControl.Content = e.ClickedItem;
    this.HamburgerMenuControl.IsPaneOpen = false;
}
  1. with binding and a converter
<controls:HamburgerMenu x:Name="HamburgerMenuControl"
                        Foreground="White"
                        PaneBackground="#FF444444"
                        IsPaneOpen="False"
                        ItemTemplate="{StaticResource MenuItemTemplate}"
                        OptionsItemTemplate="{StaticResource OptionsMenuItemTemplate}"
                        DisplayMode="CompactInline">

    <controls:HamburgerMenu.Content>
        <MultiBinding Converter="{StaticResource SelectedItemToContentConverter}">
            <Binding RelativeSource="{RelativeSource Self}"
                     Mode="OneWay"
                     Path="SelectedItem" />
            <Binding RelativeSource="{RelativeSource Self}"
                     Mode="OneWay"
                     Path="SelectedOptionsItem" />
        </MultiBinding>
    </controls:HamburgerMenu.Content>
</controls:HamburgerMenu>

and

public class SelectedItemToContentConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        // first value is selected menu item, second value is selected option item
        if (values != null && values.Length > 1)
        {
            return values[0] ?? values[1];
        }
        return null;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return targetTypes.Select(t => Binding.DoNothing).ToArray();
    }
}

You can find some more infos here

@matej89

This comment has been minimized.

Show comment
Hide comment
@matej89

matej89 Feb 10, 2017

Hi,

thanks @punker76 . I understand this. But I don't know how to call method on viewmodel. In below I send you example how I want to do this hamburger menu.

When click on item one I want to call method in viewmodel class.
Method:
private void test()
{
ChangeContentView = new ViewCaseReviewDebtViewModel(_messagingService, _frontRepository, test);
}

When click on item two I want call another method in viewmodel class.
Method:
private void test1()
{
ChangeContentView = new ViewCaseTemplateViewModel(_messagingService, _frontRepository, test);
}

Regards,
Matej

matej89 commented Feb 10, 2017

Hi,

thanks @punker76 . I understand this. But I don't know how to call method on viewmodel. In below I send you example how I want to do this hamburger menu.

When click on item one I want to call method in viewmodel class.
Method:
private void test()
{
ChangeContentView = new ViewCaseReviewDebtViewModel(_messagingService, _frontRepository, test);
}

When click on item two I want call another method in viewmodel class.
Method:
private void test1()
{
ChangeContentView = new ViewCaseTemplateViewModel(_messagingService, _frontRepository, test);
}

Regards,
Matej

@punker76

This comment has been minimized.

Show comment
Hide comment
@punker76

punker76 Feb 10, 2017

Member

@matej89 You can use the loaded event of your views

        public ViewCaseReviewDebtView()
        {
            InitializeComponent();
            this.Loaded += ViewCaseReviewDebtView_Loaded;
        }

        private void ViewCaseReviewDebtView_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            ChangeContentView = new ViewCaseReviewDebtViewModel(_messagingService, _frontRepository, test);
        }
Member

punker76 commented Feb 10, 2017

@matej89 You can use the loaded event of your views

        public ViewCaseReviewDebtView()
        {
            InitializeComponent();
            this.Loaded += ViewCaseReviewDebtView_Loaded;
        }

        private void ViewCaseReviewDebtView_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            ChangeContentView = new ViewCaseReviewDebtViewModel(_messagingService, _frontRepository, test);
        }
@tomanye

This comment has been minimized.

Show comment
Hide comment
@tomanye

tomanye Feb 11, 2017

@punker76 I share @matej89 issues Because I hate Code Behind .

tomanye commented Feb 11, 2017

@punker76 I share @matej89 issues Because I hate Code Behind .

@thoemmi

This comment has been minimized.

Show comment
Hide comment
@thoemmi

thoemmi Feb 11, 2017

Collaborator

@tomanye, @matej89 Would dependency properties like ItemCommand and OptionsItemCommand help you in your scenarios?

Collaborator

thoemmi commented Feb 11, 2017

@tomanye, @matej89 Would dependency properties like ItemCommand and OptionsItemCommand help you in your scenarios?

@tomanye

This comment has been minimized.

Show comment
Hide comment
@tomanye

tomanye Feb 11, 2017

@thoemmi can u give me simple example

tomanye commented Feb 11, 2017

@thoemmi can u give me simple example

@thoemmi

This comment has been minimized.

Show comment
Hide comment
@thoemmi

thoemmi Feb 11, 2017

Collaborator

I mean instead of the normal events

<controls:HamburgerMenu x:Name="HamburgerMenuControl"
						...
                        ItemClick="HamburgerMenuControl_OnItemClick"
                        OptionsItemClick="HamburgerMenuControl_OnItemClick"
                        ...
                        DisplayMode="CompactInline">
</controls:HamburgerMenu>

adding bindable commands, like

<controls:HamburgerMenu x:Name="HamburgerMenuControl"
						...
                        ItemCommand="{Binding ItemClickedCommand}"
                        OptionsItemCommand="{Binding OptionsItemClickedCommand}"
                        ...
                        DisplayMode="CompactInline">
</controls:HamburgerMenu>

This would enable you to handle the commands in your viewmodel.

Collaborator

thoemmi commented Feb 11, 2017

I mean instead of the normal events

<controls:HamburgerMenu x:Name="HamburgerMenuControl"
						...
                        ItemClick="HamburgerMenuControl_OnItemClick"
                        OptionsItemClick="HamburgerMenuControl_OnItemClick"
                        ...
                        DisplayMode="CompactInline">
</controls:HamburgerMenu>

adding bindable commands, like

<controls:HamburgerMenu x:Name="HamburgerMenuControl"
						...
                        ItemCommand="{Binding ItemClickedCommand}"
                        OptionsItemCommand="{Binding OptionsItemClickedCommand}"
                        ...
                        DisplayMode="CompactInline">
</controls:HamburgerMenu>

This would enable you to handle the commands in your viewmodel.

@matej89

This comment has been minimized.

Show comment
Hide comment
@matej89

matej89 Feb 11, 2017

@thoemmi this is what I want.
@tomanye I also hate code behind.

Thank you for help.

matej89 commented Feb 11, 2017

@thoemmi this is what I want.
@tomanye I also hate code behind.

Thank you for help.

@punker76

This comment has been minimized.

Show comment
Hide comment
@punker76
Member

punker76 commented Feb 11, 2017

@thoemmi added to #2847

@punker76 punker76 added this to the 1.5.0 milestone Feb 11, 2017

@punker76 punker76 self-assigned this Feb 11, 2017

@punker76 punker76 referenced this issue Feb 11, 2017

Merged

HamburgerMenu changes/improvements #2847

6 of 6 tasks complete
@tomanye

This comment has been minimized.

Show comment
Hide comment
@tomanye

tomanye Feb 11, 2017

@thoemmi but those Commands not shipped with HamburgerMenu

tomanye commented Feb 11, 2017

@thoemmi but those Commands not shipped with HamburgerMenu

@thoemmi

This comment has been minimized.

Show comment
Hide comment
@thoemmi

thoemmi Feb 11, 2017

Collaborator

@tomanye That was just a proposal, it's not implemented yet.

Collaborator

thoemmi commented Feb 11, 2017

@tomanye That was just a proposal, it's not implemented yet.

@punker76

This comment has been minimized.

Show comment
Hide comment
@punker76

punker76 Feb 14, 2017

Member

@matej89 @tomanye another way without that we implement such commands is to use the interactivity stuff

<controls:HamburgerMenu x:Name="HamburgerMenuControl"
                        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
                        SelectedIndex="1"
                        Margin="20"
                        Foreground="White"
                        HamburgerWidth="48"
                        ItemTemplate="{StaticResource HamburgerMenuImageItem}"
                        OptionsItemTemplate="{StaticResource HamburgerMenuItem}"
                        PaneBackground="#FF444444">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="ItemClick">
            <i:InvokeCommandAction Command="{Binding YourViewModel.YourItemClickCommand}"
                                   CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedItem}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="OptionsItemClick">
            <i:InvokeCommandAction Command="{Binding YourViewModel.YourOptionsItemClickCommand}"
                                   CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedOptionsItem}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</controls:HamburgerMenu

/cc @thoemmi

Member

punker76 commented Feb 14, 2017

@matej89 @tomanye another way without that we implement such commands is to use the interactivity stuff

<controls:HamburgerMenu x:Name="HamburgerMenuControl"
                        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
                        SelectedIndex="1"
                        Margin="20"
                        Foreground="White"
                        HamburgerWidth="48"
                        ItemTemplate="{StaticResource HamburgerMenuImageItem}"
                        OptionsItemTemplate="{StaticResource HamburgerMenuItem}"
                        PaneBackground="#FF444444">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="ItemClick">
            <i:InvokeCommandAction Command="{Binding YourViewModel.YourItemClickCommand}"
                                   CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedItem}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="OptionsItemClick">
            <i:InvokeCommandAction Command="{Binding YourViewModel.YourOptionsItemClickCommand}"
                                   CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedOptionsItem}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</controls:HamburgerMenu

/cc @thoemmi

@thoemmi

This comment has been minimized.

Show comment
Hide comment
@thoemmi

thoemmi Feb 14, 2017

Collaborator

That should do the trick, as System.Windows.Interactivity is required anyway.

However, standard controls like Button or MenuItem support ICommand out of the box, so why not HamburgerMenuor HamburgerMenuItem as well? (By the way, I don't like the HamburgerMenuItem.Tag property, it reminds me of my WinForms days too much 😉)

Collaborator

thoemmi commented Feb 14, 2017

That should do the trick, as System.Windows.Interactivity is required anyway.

However, standard controls like Button or MenuItem support ICommand out of the box, so why not HamburgerMenuor HamburgerMenuItem as well? (By the way, I don't like the HamburgerMenuItem.Tag property, it reminds me of my WinForms days too much 😉)

@punker76

This comment has been minimized.

Show comment
Hide comment
@punker76

punker76 Feb 14, 2017

Member

@thoemmi thx, you're right, the commands could be useful (Command on MenuItem and ItemCommand and OptionsItemCommand on the HamburgerMenu itself). The tag property comes from the UWP toolkit code, I will add another property called Content which should be obvious, and sign the Tag property as obsolete (you know, it's a breaking change).

Member

punker76 commented Feb 14, 2017

@thoemmi thx, you're right, the commands could be useful (Command on MenuItem and ItemCommand and OptionsItemCommand on the HamburgerMenu itself). The tag property comes from the UWP toolkit code, I will add another property called Content which should be obvious, and sign the Tag property as obsolete (you know, it's a breaking change).

@tomanye

This comment has been minimized.

Show comment
Hide comment
@tomanye

tomanye Feb 15, 2017

@punker76 thanks , it Worked

tomanye commented Feb 15, 2017

@punker76 thanks , it Worked

@smzuber

This comment has been minimized.

Show comment
Hide comment
@smzuber

smzuber Mar 21, 2017

Hi,

I am using alpha version and I tried the above mentioned idea to use event triggers but that does not seem to work. Any idea what I am doing wrong ?

<metro:HamburgerMenu
x:Name="HamburgerMenuControl"
DisplayMode="CompactInline"
Foreground="White"
IsPaneOpen="False"
ItemTemplate="{StaticResource MenuItemTemplate}"
OptionsItemTemplate="{StaticResource OptionsMenuItemTemplate}"
PaneBackground="#FF444444">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ItemClick">
<i:InvokeCommandAction Command="{Binding HamburgerMenu.ItemCommand}"
CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedItem}" />
</i:EventTrigger>
<i:EventTrigger EventName="OptionsItemClick">
<i:InvokeCommandAction Command="{Binding HamburgerMenu.OptionsItemCommand}"
CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedOptionsItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>

Thanks,
Zuber

smzuber commented Mar 21, 2017

Hi,

I am using alpha version and I tried the above mentioned idea to use event triggers but that does not seem to work. Any idea what I am doing wrong ?

<metro:HamburgerMenu
x:Name="HamburgerMenuControl"
DisplayMode="CompactInline"
Foreground="White"
IsPaneOpen="False"
ItemTemplate="{StaticResource MenuItemTemplate}"
OptionsItemTemplate="{StaticResource OptionsMenuItemTemplate}"
PaneBackground="#FF444444">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ItemClick">
<i:InvokeCommandAction Command="{Binding HamburgerMenu.ItemCommand}"
CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedItem}" />
</i:EventTrigger>
<i:EventTrigger EventName="OptionsItemClick">
<i:InvokeCommandAction Command="{Binding HamburgerMenu.OptionsItemCommand}"
CommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedOptionsItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>

Thanks,
Zuber

@matej89

This comment has been minimized.

Show comment
Hide comment
@matej89

matej89 Mar 22, 2017

matej89 commented Mar 22, 2017

@smzuber

This comment has been minimized.

Show comment
Hide comment
@smzuber

smzuber Mar 23, 2017

That didnt't work either. I dont have access to code behind, so I have to do this in XAML only. Thanks

smzuber commented Mar 23, 2017

That didnt't work either. I dont have access to code behind, so I have to do this in XAML only. Thanks

@Gohico

This comment has been minimized.

Show comment
Hide comment
@Gohico

Gohico Oct 11, 2017

Hi,

I started using the hamburger menu and works nicely so far. But (warning - new to WPF) I have problems understanding the ItemCommandParamter for the menu.

So my xaml looks like this right now:

   <Controls:HamburgerMenu x:Name="HamburgerMenuControl"
   Foreground="#f3f3f7"
   PaneBackground="#00437b"
   IsPaneOpen="False"
   ItemTemplate="{StaticResource MenuItemTemplate}"
   OptionsItemTemplate="{StaticResource OptionsMenuItemTemplate}"
   DisplayMode="CompactInline"
   ItemCommand="{Binding SomethingSelected}"
   ItemCommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedItem}"
   ItemClick="HamburgerMenuControl_OnItemClick"
   OptionsItemClick="HamburgerMenuControl_OnItemClick">
   ...
   </Controls:HamburgerMenu>

The normal button click events work fine of course. But I'd like to move that logic into the corresponding view models.
The binding itself also works - so SomethingSelectedgets called in my ViewModel. But I don't understand how the ItemCommandParameter gets passed along.

It would be awesome if you could update one of your examples to show how to use the binding with viewmodels with ItemCommand and especially ItemCommandParamter.

Thanks in advance
Nico

Gohico commented Oct 11, 2017

Hi,

I started using the hamburger menu and works nicely so far. But (warning - new to WPF) I have problems understanding the ItemCommandParamter for the menu.

So my xaml looks like this right now:

   <Controls:HamburgerMenu x:Name="HamburgerMenuControl"
   Foreground="#f3f3f7"
   PaneBackground="#00437b"
   IsPaneOpen="False"
   ItemTemplate="{StaticResource MenuItemTemplate}"
   OptionsItemTemplate="{StaticResource OptionsMenuItemTemplate}"
   DisplayMode="CompactInline"
   ItemCommand="{Binding SomethingSelected}"
   ItemCommandParameter="{Binding ElementName=HamburgerMenuControl, Path=SelectedItem}"
   ItemClick="HamburgerMenuControl_OnItemClick"
   OptionsItemClick="HamburgerMenuControl_OnItemClick">
   ...
   </Controls:HamburgerMenu>

The normal button click events work fine of course. But I'd like to move that logic into the corresponding view models.
The binding itself also works - so SomethingSelectedgets called in my ViewModel. But I don't understand how the ItemCommandParameter gets passed along.

It would be awesome if you could update one of your examples to show how to use the binding with viewmodels with ItemCommand and especially ItemCommandParamter.

Thanks in advance
Nico

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment