Skip to content
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 · 21 comments
Closed

HamburgerMenu Command MVVM #2848

matej89 opened this issue Feb 10, 2017 · 21 comments
Assignees
Milestone

Comments

@matej89
Copy link

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
Copy link
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

@matej89
Copy link
Author

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
Copy link
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);
        }

@tomanye
Copy link

tomanye commented Feb 11, 2017

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

@thoemmi
Copy link
Collaborator

thoemmi commented Feb 11, 2017

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

@tomanye
Copy link

tomanye commented Feb 11, 2017

@thoemmi can u give me simple example

@thoemmi
Copy link
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
Copy link
Author

matej89 commented Feb 11, 2017

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

Thank you for help.

@punker76
Copy link
Member

@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
@tomanye
Copy link

tomanye commented Feb 11, 2017

@thoemmi but those Commands not shipped with HamburgerMenu

@thoemmi
Copy link
Collaborator

thoemmi commented Feb 11, 2017

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

@punker76
Copy link
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
Copy link
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 HamburgerMenu or HamburgerMenuItem as well? (By the way, I don't like the HamburgerMenuItem.Tag property, it reminds me of my WinForms days too much 😉)

@punker76
Copy link
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).

@tomanye
Copy link

tomanye commented Feb 15, 2017

@punker76 thanks , it Worked

@smzuber
Copy link

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
Copy link
Author

matej89 commented Mar 22, 2017 via email

@smzuber
Copy link

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
Copy link

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

@ecxdev
Copy link

ecxdev commented Nov 20, 2018

Hi,

I have a similar problem like @Gohico
How I can to set content after execute my Command?
My CommanAction looks like this:

private void HamburgerMenuItem_ClickCommand(object obj)
{
    var hamburgerMenu = (HamburgerMenu)obj;
    var itm = (HamburgerMenuItem) hamburgerMenu.SelectedItem;
    hamburgerMenu.Content = itm.Tag;
}

This code work, but not, as expected, because DataTemplate HamburgerMenuItem is completely replaced.
How do I get an object from ClickedItem property of ItemClickEventArgs e parameter into ViewModel?

@Gohico
Copy link

Gohico commented Nov 20, 2018

Not sure if this is of any help as I haven't touched this since... Last year...
But I ended up doing this (sort of work around):

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

            <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>

            <!--  Items  -->
            <!-- Symbol enum: https://msdn.microsoft.com/library/windows/apps/dn252842 -->
            <Controls:HamburgerMenu.ItemsSource>
                <Controls:HamburgerMenuItemCollection>

                    <Tools:CustomHamburgerMenuIconItem 
                        Icon="{iconPacks:PackIconMaterial Kind=Home}"
                        Label="Home"
                        ToolTip="Home"
                        >
                        <Tools:CustomHamburgerMenuIconItem.Tag>
                            <View:HomeView />
                        </Tools:CustomHamburgerMenuIconItem.Tag>
                    </Tools:CustomHamburgerMenuIconItem>

                </Controls:HamburgerMenuItemCollection>
            </Controls:HamburgerMenu.ItemsSource>

            <!--  Content  -->
            <Controls:HamburgerMenu.ContentTemplate>
                <DataTemplate DataType="{x:Type Controls:HamburgerMenuItem}">
                    <Grid x:Name="TheContentGrid">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="48" />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0"
                                Background="#00437b">
                            <TextBlock x:Name="Header"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"
                                       FontSize="24"
                                       Foreground="White"
                                       Text="{Binding Label}" />
                        </Border>
                        <ContentControl x:Name="TheContent"
                                        Grid.Row="1"
                                        Focusable="False"
                                        Background="#f3f3f7"
                                        Foreground="{DynamicResource BlackBrush}"
                                        Content="{Binding Tag}" />
                    </Grid>
                </DataTemplate>
            </Controls:HamburgerMenu.ContentTemplate>

        </Controls:HamburgerMenu>

Then in my UiBlabla.xaml.cs (so NOT the datamodel but the code behind the UI) I have the eventhandler
as

private void HamburgerMenuControl_OnItemClick(object sender, ItemClickEventArgs e)

So the views are loaded based on the 'Controls:HamburgerMenuItemCollection' ; in cases where no view is needed I just use the event handler.
From the event handle you also get the object (so ItemClickEventArgs).

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

No branches or pull requests

7 participants