Search code examples
c#wpfmvvmcontrolsdatatemplate

Place data template and styles to generated buttons


What I'll to do

I'll add some SubMenuButtons (code below) on an ItemControl generated by code behind in the view model of the page where the ItemControl is placed on. I've tried 2 solutions and give me 2 different results.


1st solution I've tried

Code

Here is the class I've made:

public class SubMenuButton : Button
{
    public Page TargetPage { get; set; }
    public FontAwesomeIcon Icon { get; set; }
    public string Text { get; set; }

    public SubMenuButton()
    {
        Style = App.Current.FindResource("submenuButtonStyle") as Style; 
        // comment line above for 2nd solution I've tried
    }

    public SubMenuButton(string text, Page targetPage, ICommand command, 
                                                            FontAwesomeIcon icon) : this()
    {
        Icon = icon;
        Text = text;
        TargetPage = targetPage;
        CommandParameter = targetPage;
        Command = command;
    }
}

For this class I've also made a Style for display the icon and the text and ser also some properties to make it better.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
                    xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
                    xmlns:fa="http://schemas.fontawesome.io/icons/"
                    xmlns:uie="clr-namespace:Provar2.Client.DesktopApp.UIElements">

    <Style x:Key="transparantButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="{x:Null}"/>
        <Setter Property="Width" Value="auto"/>
        <Setter Property="Height" Value="auto"/>
        <Setter Property="Margin" Value="0,5"/>
        <Setter Property="Width" Value="auto"/>
        <Setter Property="VerticalAlignment" Value="Top"/>
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Cursor" Value="Hand"/>
    </Style>

    <Style x:Key="submenuButtonStyle" BasedOn="{StaticResource transparantButtonStyle}" 
           TargetType="{x:Type uie:SubMenuButton}">

        <Setter Property="CommandParameter" Value="{Binding TargetPage}" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type uie:SubMenuButton}">
                    <Canvas Height="auto">
                        <fa:FontAwesome Icon="{Binding Icon}" Width="30" Height="30"/>
                        <TextBlock Text="{Binding Text}"/>
                    </Canvas>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="White" />
                <Setter Property="Foreground" Value="Black" />
            </Trigger>
        </Style.Triggers>
    </Style>
</ResourceDictionary>

This is where the SubMenuButton must be generated

<ItemsControl Grid.Column="0" Grid.Row="1" VerticalAlignment="Stretch" 
              HorizontalAlignment="Stretch" Background="Gray" 
              ItemsSource="{Binding SubMenuItems}" />

Result

But for this solution I've got this exception:

BindingExpression: Text property not found on object SubMenuPageViewModel.

BindingExpression:
Path=Text; 
DataItem='SubMenuPageViewModel' (HashCode=28642977); 

Target element is TextBlock target property is Text (type String)


2nd solution I've tried

Code

I've also tried to add a property ItemTemplate to my ItemsControl.

<ItemsControl ItemTemplate="{StaticResource submenuButtonTemplate}" Grid.Column="0" 
              Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
              Background="Green" ItemsSource="{Binding SubMenuItems}" />

I've also comment some lines to my code inside empty constructor from SubMenuButton.

and add this data template

<DataTemplate x:Key="submenuButtonTemplate" DataType="{x:Type scr:SubMenuButton}">
    <Canvas Height="auto">
        <fa:FontAwesome Icon="{Binding Icon}" Width="30" Height="30"/>
        <TextBlock Text="{Binding Text}"/>
    </Canvas>
</DataTemplate>

Result

Now I don't have the exception but I've got this:

UI

The green rectangle is my ItemTemplate, the 2 SubMenuButtons (because I generate 2 SubMenuButtons) I've circled with a yellow pen.


Question

Could you fix my problem?


Solution

  • TextBlock tries to get Text property from DataContext, which doesn't have it. Add RelativeSource parameter to point that Text belong to uie:SubMenuButton

    <TextBlock Text="{Binding Text, RelativeSource={RelativeSource AncestorType=uie:SubMenuButton}}"/>
    

    and if Text is implemented as DependencyProperty it will notify about changes, support binding, etc