microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.35k stars 678 forks source link

XamlUICommand IconSource doesn't bind to button #4517

Open brechtb86 opened 3 years ago

brechtb86 commented 3 years ago

Describe the bug

When using an IconSource in a XamlUICommand bound to a Button then the icon is not visible.

Steps to reproduce the bug

  1. Bind a XamlUICommand with an IconSource to a button. The icon isn't visible.

Expected behavior

The icon should be visible.

Screenshots

Imgur

Version info

Nuget package version: [WinUI 3 - Project Reunion 0.5 Preview: 0.5.0-prerelease]

Windows app type

UWP Win32
?  Yes
Windows 10 version Saw the problem?
Insider Build (xxxxx)  
October 2020 Update (19042) Yes
May 2020 Update (19041)  
November 2019 Update (18363)  
May 2019 Update (18362)  
October 2018 Update (17763)  
April 2018 Update (17134)  
Fall Creators Update (16299)  
Creators Update (15063)  

Code

A small project can be found in my repository: https://github.com/brechtb86/microsoft-ui-xaml-issues

XamlCommandBindingIconSourceIssuePage.xaml:

<Page
    x:Class="WinUI3Issues.XamlCommandBindingIconSourceIssuePage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUI3Issues"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    DataContext="{Binding Path=XamlCommandBindingIconSourceIssueViewModel, Source={StaticResource ViewModelLocator}}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />

        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Margin="0,16,0,0" Orientation="Vertical">
            <TextBlock>PickFolderWithIcon (with binding):</TextBlock>
            <Button Command="{Binding Path=PickFolderWithIcon}" />
        </StackPanel>
        <StackPanel Grid.Row="1" Margin="0,16,0,0" Orientation="Vertical">
            <TextBlock>PickFolderWithIcon (without binding):</TextBlock>
            <Button>
                <SymbolIcon Symbol="Folder" />
            </Button>
        </StackPanel>
        <StackPanel Grid.Row="2" Margin="0,16,0,0" Orientation="Vertical">
            <TextBlock>PickFolderWithLabel (with binding):</TextBlock>
            <Button Command="{Binding Path=PickFolderWithLabel}" />
        </StackPanel>
    </Grid>
</Page>

XamlCommandBindingIconSourceIssueViewModel:

public class XamlCommandBindingIconSourceIssueViewModel : BaseViewModel
{
    public XamlUICommand PickFolderWithIcon
    {
        get;
        private set;
    }

    public XamlUICommand PickFolderWithLabel
    {
        get;
        private set;
    }

    public XamlCommandBindingIconSourceIssueViewModel()
    {
        this.PickFolderWithIcon = new XamlUICommand
        {
            IconSource = new SymbolIconSource
            {
                Symbol = Symbol.Folder
            },
            Description = "Select a movie folder to add...",
        };

        this.PickFolderWithIcon.ExecuteRequested += async (command, args) =>
        {
            // Do something
        };

        this.PickFolderWithLabel = new XamlUICommand
        {
            Label = "Pick folder",
            Description = "Select a movie folder to add...",
        };

        this.PickFolderWithLabel.ExecuteRequested += async (command, args) =>
            {
                // Do something
            };
    }
}
brechtb86 commented 3 years ago

Original comment https://github.com/microsoft/microsoft-ui-xaml/issues/4074#issue-798776765

StephenLPeters commented 3 years ago

@brechtb86 does using x:Bind instead work?

brechtb86 commented 3 years ago

@StephenLPeters I thought that x:Bind only binds to dependency properties on the Page/UserControl code-behind. As you can see on this screenshot:

brechtb86 commented 3 years ago

@StephenLPeters I added an x:Bind in my Xaml and a XamlUICommand in the codebehind of that page just to see if it works to my sample project, and this doesn't work either. I prefer to use MVVM so that is why my example only bound to my viewmodel.

Page.xaml

<Page
    x:Class="WinUI3Issues.XamlCommandBindingIconSourceIssuePage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUI3Issues"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    DataContext="{Binding Path=XamlCommandBindingIconSourceIssueViewModel, Source={StaticResource ViewModelLocator}}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />

        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Margin="0,16,0,0" Orientation="Vertical">
            <TextBlock>PickFolderWithIcon (with Binding):</TextBlock>
            <Button Command="{Binding Path=PickFolderWithIcon}" />
        </StackPanel>
        <StackPanel Grid.Row="1" Margin="0,16,0,0" Orientation="Vertical">
            <TextBlock>PickFolderWithIcon (no Binding):</TextBlock>
            <Button>
                <SymbolIcon Symbol="Folder" />
            </Button>
        </StackPanel>
        <StackPanel Grid.Row="2" Margin="0,16,0,0" Orientation="Vertical">
            <TextBlock>PickFolderWithLabel (with Binding):</TextBlock>
            <Button Command="{Binding Path=PickFolderWithLabel}" />
        </StackPanel>
        <StackPanel Grid.Row="3" Margin="0,16,0,0" Orientation="Vertical">
            <TextBlock>PickFolderWithIcon (with x:Bind):</TextBlock>
            <Button Command="{x:Bind Path=PickFolderWithIcon}" />
        </StackPanel>
    </Grid>
</Page>

Page.cs

public sealed partial class XamlCommandBindingIconSourceIssuePage : Page
{
    public XamlUICommand PickFolderWithIcon
    {
        get;
        private set;
    }

    public XamlCommandBindingIconSourceIssuePage()
    {
        this.PickFolderWithIcon = new XamlUICommand
        {
            IconSource = new SymbolIconSource
            {
                Symbol = Symbol.Folder
            },
            Description = "Select a movie folder to add...",
        };

        this.PickFolderWithIcon.ExecuteRequested += async (command, args) =>
        {
            // Do something
        };

        this.InitializeComponent();
    }
}
brechtb86 commented 3 years ago

I updated the project to Project Reunion 0.8. Problem still remains, anyone any thoughts?

chrisglein commented 3 years ago

This repros on UWP XAML, so this isn't a WinUI3 issue. It doesn't have anything to do with binding either. Note that this doesn't work:

    <Button>
      <Button.Command>
        <XamlUICommand Label="Pick folder" Description="Select a movie folder to add...">
          <XamlUICommand.IconSource>
            <SymbolIconSource Symbol="Folder"/>
          </XamlUICommand.IconSource>
        </XamlUICommand>
      </Button.Command>
    </Button>

Nor does this:

    <Button x:Name="_myButton"/>
        public MainPage()
        {
            this.InitializeComponent();

            _myButton.Command = new XamlUICommand
            {
                Description = "My description",
                IconSource = new SymbolIconSource
                {
                    Symbol = Symbol.Folder
                }
            };
        }

The example you have above that does work isn't using a Command at all. It's providing an SymbolIcon directly to the Button as content. Which is different from giving the Button a Command having it know to extract both the Label and IconSource property values to display an image and text combination.

This is actually a feature request for the Button control to support the icon part of a Command.

StephenLPeters commented 3 years ago

I think we could do something like, check if the content property of the button is unset, if so, check the command propertly for an icon source. However this change would require system xaml changes which wont be funded.

App Bar button already does this, or you could hook up the properties yourself.