0

I'm new to C# and wpf so please don't criticize if this is a dumb question.

I have Image and TextBlock inside a DockPanel, then I have a ComboBox which controls where the image would appear around the text. ComboBox items are ("left of text", "right of text", "above the text", "below the text", "center")

I was able to do left, right, top, and bottom by binding DockPanel.Dock but for the center, I need to put the image behind the text (overlaying them) and DockPanel doesn't allow me to do that. I wanted to use Canvas but I was specifically asked not to for a different reason (textwrapping problem, etc.)

Now I just really need to overlay the image and text when "center" is selected from combobox.

xaml

<DockPanel>
    <Image Source="{Binding Path=ImageSource, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="{Binding Path=ImagePlacementDisplay, UpdateSourceTrigger=PropertyChanged}"/>
    <TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 

view model

    public string SelectedImagePlacement
    {
        get { return _ex2.ImagePlacement; }
        set
        {
            _ex2.ImagePlacement = value;
            OnPropertyChanged("SelectedImagePlacement");
            OnPropertyChanged("ImagePlacementDisplay");
        }
    }

    public string ImagePlacementDisplay
    {
        get
        {
            switch (SelectedImagePlacement)
            {
                case "0": 
                    return "Left";
                case "1": 
                    return "Right";
                case "2":
                    return "Top";
                case "3":
                    return "Bottom";
                case "4":
                    return "Center"; //not working
                default:
                    return "Right";
            }
        }
        set
        {
            OnPropertyChanged("ImagePlacementDisplay");
        }
    }
TechNoob
  • 1
  • 1
  • You forgot to show xaml. The z-order is same as order of panel children, put `Image` first, then `TextBox` and the latter will be *above*. Or you can use [ZIndex](https://stackoverflow.com/q/5450985/1997232) to change it. – Sinatr Aug 19 '21 at 07:04
  • @Sinatr I included snippets of my code but I'm not sure if it helped or it just made my question more confusing. I tried using Panel.ZIndex but it wasn't just what I wanted. – TechNoob Aug 19 '21 at 07:31
  • So you want both controls to [occupy the middle](https://stackoverflow.com/q/28356153/1997232) and overlap? You can make an own panel with custom enum to control alignment. Or you can forget about `DockPanel`, use `Grid` and arrange controls using data triggers. – Sinatr Aug 19 '21 at 08:08

1 Answers1

0

You can use data triggers to modify control position.

Below is 2x2 Grid and depending on ComboBox.SelectedIndex value the data trigger will arrange location of Image (simulated with yellow grid) and TextBlock.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid Background="Yellow">
        <Grid.Style>
            <Style TargetType="Grid">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="0">
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                        <Setter Property="Grid.RowSpan" Value="2" />
                        <Setter Property="Grid.ColumnSpan" Value="1" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="1">
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="1" />
                        <Setter Property="Grid.RowSpan" Value="2" />
                        <Setter Property="Grid.ColumnSpan" Value="1" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="2">
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                        <Setter Property="Grid.RowSpan" Value="1" />
                        <Setter Property="Grid.ColumnSpan" Value="2" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="3">
                        <Setter Property="Grid.Row" Value="1" />
                        <Setter Property="Grid.Column" Value="0" />
                        <Setter Property="Grid.RowSpan" Value="1" />
                        <Setter Property="Grid.ColumnSpan" Value="2" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="4">
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                        <Setter Property="Grid.RowSpan" Value="2" />
                        <Setter Property="Grid.ColumnSpan" Value="2" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Grid.Style>
    </Grid>
    <TextBlock Text="Bla bla bla" VerticalAlignment="Center" HorizontalAlignment="Center">
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="0">
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="1" />
                        <Setter Property="Grid.RowSpan" Value="2" />
                        <Setter Property="Grid.ColumnSpan" Value="1" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="1">
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                        <Setter Property="Grid.RowSpan" Value="2" />
                        <Setter Property="Grid.ColumnSpan" Value="1" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="2">
                        <Setter Property="Grid.Row" Value="1" />
                        <Setter Property="Grid.Column" Value="0" />
                        <Setter Property="Grid.RowSpan" Value="1" />
                        <Setter Property="Grid.ColumnSpan" Value="2" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="3">
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                        <Setter Property="Grid.RowSpan" Value="1" />
                        <Setter Property="Grid.ColumnSpan" Value="2" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SelectedIndex, ElementName=combobox}" Value="4">
                        <Setter Property="Grid.Row" Value="0" />
                        <Setter Property="Grid.Column" Value="0" />
                        <Setter Property="Grid.RowSpan" Value="2" />
                        <Setter Property="Grid.ColumnSpan" Value="2" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
    <ComboBox x:Name="combobox" VerticalAlignment="Bottom" Grid.Row="1" SelectedIndex="0">
        <ComboBoxItem Content="Left"/>
        <ComboBoxItem Content="Right"/>
        <ComboBoxItem Content="Up"/>
        <ComboBoxItem Content="Down"/>
        <ComboBoxItem Content="Center"/>
    </ComboBox>
</Grid>

Demo:

It may not be the best looking solution, but it demonstrates the power of data triggers and maybe teach you a bit of layouting (very important skill in wpf).

A better approach would be to create custom Panel with custom dependency property of enum type to control children arrangement.

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • Thank you for providing an alternative. But this approach is hardwired to UI and will be difficult to maintain if I make some changes. I still need to add Margin (a numeric box controls this) between them, a frame for the text, etc. So I'm guessing I need to try making a custom Panel just like you said. – TechNoob Aug 19 '21 at 09:32