WPF MenuItem.Icon dimensions

2019-08-14 06:59发布

问题:

So I have a MenuItem (within a ContextMenu, if it really makes a difference, don't think it does). I want to display an icon within the MenuItem using the MenuItem.Icon property. Here is XAML code that does this:

<MenuItem Header="MenuItem1">
    <MenuItem.Icon>
        <Image Source="[pathtoimage]"/>
     </MenuItem.Icon>
</MenuItem>

This works. But what if I want to specify the size of the icon with Height and Width ? Search on the web reveals that it should be 32, some say it should be 20. 20 works the best for me, but I don't want to hardcode 20. I want to set it to the same height as the MenuItem, given the fact that on a different resolution the actual height might be different. So, I tried the following:

<MenuItem x:Name="menuItem1" Header="MenuItem1">
    <MenuItem.Icon>
        <Image Source="[pathtoimage]"
               Height="{Binding Path=ActualHeight, ElementName=menuItem1}"
               Width="{Binding Path=ActualHeight, ElementName=menuItem1}"
     </MenuItem.Icon>
</MenuItem>

You would think that it should work, but it didn't. The question is why ? I am able to get my desired appearance if I set both Width and Height to 20. But if the system font size gets changed, then 20x20 won't be ideal anymore. It would be ideal to do this in the XAML, because I am sure there are a lot of ways of handling this in the code-behind.

回答1:

Icon size and icon area width are hardcoded inside the MenuItem templates and styles like the following.

            <ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>

So if you want dynamic icon size you need to copy all the templates and styles related to MenuItem from one of the SystemThemes (e.g. aero2.normalcolor.xaml) and then you can customize them for your needs. In your case it would need vast amount of changes in those templates and styles. Unfortunately there are no simpler ways to do this. And it is not scalable either since you would stuck with just one System Theme unless you change all of them.

Similar things were suggested in the related topics here: WPF - How to style the menu control to remove the left margin? or System.Windows.Controls.MenuItem without icon area.



回答2:

Well basically the problem is due to the MenuItem Header, the Header is of type object and you add a string so it has no Height/Width, so the MenuItem ActualHeight will return Auto

If you were to add a Textblock as the Header this will give the MenuItem a Height that can be used to set your image size.

<MenuItem x:Name="menuItem1" >
    <MenuItem.Header>
        <TextBlock Text="MenuItem1" />
    </MenuItem.Header>
    <MenuItem.Icon>
        <Image  Height="{Binding Header.ActualHeight, ElementName=menuItem1}"
                Width="{Binding Header.ActualHeight, ElementName=menuItem1}" />
    </MenuItem.Icon>
</MenuItem>

So with this in mind you could easily make a ItemContainerTemplate for the MenuItem so the all behave in this fashion