开发者

XAML: Placing controls over an Image

开发者 https://www.devze.com 2023-03-07 08:29 出处:网络
I need to display an Image in a Grid cell. On top of the image, a StackPanel of controls (such as zoom, brightness etc) has to be added at the bottom right corner of the image. How can this be done. I

I need to display an Image in a Grid cell. On top of the image, a StackPanel of controls (such as zoom, brightness etc) has to be added at the bottom right corner of the image. How can this be done. I am doing the following, but not sure how to position the StackPanel of controls on the lower right corner of the im开发者_JS百科age. The position needs to be maintained even if user resizes browser window.

<Grid Grid.Column="1" Height="387" HorizontalAlignment="Left" Name="Image_Border" VerticalAlignment="Top" Width="799">  
      <Border BorderBrush="Black" BorderThickness="1" Grid.ColumnSpan="2" Height="Auto" HorizontalAlignment="Left" Name="Border_Image" VerticalAlignment="Top" Width="Auto" >
           <Canvas Height="Auto" HorizontalAlignment="Left" Name="ImageCanvas"  VerticalAlignment="Top" Background="Transparent" Width="Auto">                               
                <Image Canvas.Left="0" Canvas.Top="0" Height="Auto" Name="imageName" Stretch="None" Width="Auto" HorizontalAlignment="Left" VerticalAlignment="Top"/>
           </Canvas>
      </Border>     
      <StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Right">
        <!--Stackpanel of controls to be placed at the lower right corner image -->
      </StackPanel>                  
</Grid>


The intent of the UI is a little unclear so I'll hold back from giving a more complete answer for now. However if you simply had an image and you want to overlay the bottom right of this image with controls then a Grid is the solution:-

<Grid>
    <Image x:Name="img" Stretch="None" />
    <StackPanel x:Name="control" VerticalAlignment="Bottom" HorizontalAlignment="Right">
        <!-- controls here -->
    </StackPanel>
</Grid>

This grid will size to whatever size the Image is (unless its smaller the the controls panel), the controls panel will float on top in the bottom right corner of the image. This is case of less is more let the components do the work.

Since one of your controls is "Zoom" I suspect you will have other issues to solve which might ultimately make this problem moot but the above is the essence of what you need for now.


I solved a similar problem in WPF by overriding protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)

I needed to shuffle things around as the user enlarged the screen, so I could resize an image up maintaining aspect ratio and to make room for 3x and 4x zoom had to move controls around and then overlay some on the image.

I achieved this by having an outermost Canvas element with nested inside it a couple of Grids to handle most of the layout and the sets of controls that needed moving around or overlaying in StackPanels.

Note how I tried to avoid too much hardcoding by driving the logic from the initial layout - if you rearrange things a bit in the XAML it should still cope by sizing things relative to a couple of key objects.

    /**
     * Stores sizes used by OnRenderSizeChanged() to measure relative changes, based off the size of elements initially drawn.
     * 
     * Everything is driven by the size and position of the grid rightSideControls because that is immediately
     * adjacent to the visible bitmap firebar when we open.
     * 
     * SEImagesBitmap is drawn in the background so its size can actually be way too big
     * 
    */
    private void InitSizesOnceConstructed()
    {
        if (gotSizes)
        {
            return;
        }

        gotSizes = true;
        initialBitmapSize.Height = SEImagesBitmap.ActualHeight;
        initialBitmapSize.Width = SEImagesBitmap.ActualWidth;
        initialRightSideControlsBounds = new Rect(
                                            Canvas.GetLeft(rightSideControls), 
                                            0,
                                            rightSideControls.ActualWidth, 
                                            rightSideControls.ActualHeight);
        initialWindowExtra.Width = Width - (initialRightSideControlsBounds.Right + 4);
        initialWindowExtra.Height = Height - (Canvas.GetTop(bottomControls) + bottomControls.ActualHeight);
    }

    /**
     * Moves things around to fit once the window is big enough for the main image to rescale, starting from trying to fit at scale 1 and moving up.
     * 
     * Relies heavily on SizeAtScale() to decide if that scale will fit, but the actual layout is done here.
     * 
     * May move controls on top of the image, so changes text color to white to make it visible on the image typical black margin.
     * 
     * Standard event invoked after the size is changed for any reason.
     * @warning if you change the layout logic in here must change SizeAtScale() to match!
     */
    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        if (gotSizes)
        {
            if (lastResizePutControlsOverImage)
            {  // cleanup color changes because we might not put it back
                lastResizePutControlsOverImage = false;
                TEXT_Mask.Foreground = Brushes.Black;
            }

            Size newSize = sizeInfo.NewSize;

            // find the largest scale that will fit, regardless of whether we just resized up or down
            int scale = 1;
            Size minSizeForScale = new Size(0, 0);  // fake init because doesn't like not having one, but always at least once through loop below
            for (; scale <= MAX_IMAGE_SCALE; scale++) 
            {
                minSizeForScale = SizeAtScale(scale);
                if (newSize.Height < minSizeForScale.Height ||
                    newSize.Width < minSizeForScale.Width)
                {
                    scale = Math.Max(1, scale - 1);
                    break;
                }
            }

            if (drawingCanvas.Scale != scale || justForcedMaximize)
            {
                justForcedMaximize = false;
                bool putBottomControlsOnImage = false;
                if (newSize.Height > 1024)
                {
                    // do our best to fit on to a 1050 screen
                    if (WindowState == WindowState.Maximized && scale == 3)
                    {
                        scale = 4;
                    }

                    putBottomControlsOnImage = true;
                }

                drawingCanvas.SetScale(scale);
                drawingCanvas.Width = scale * 256;
                drawingCanvas.Height = scale * 256;
                SEImagesBitmap.Width = scale * initialBitmapSize.Width;
                SEImagesBitmap.Height = scale * initialBitmapSize.Height;

                Canvas.SetLeft(fireLegendGrid, SEImagesBitmap.Width);
                fireLegendGrid.Height = SEImagesBitmap.Height;
                Canvas.SetLeft(bottomLeftControls, 0);
                double newTopForBottomLeftControls = SEImagesBitmap.Height;
                if (putBottomControlsOnImage)
                {
                    newTopForBottomLeftControls -= 40;  // reasonably elegant appearance on bottom of image
                    if (newSize.Height < 1040)
                    {
                        // taskbar on a 1050 screen, take a bit more off
                        newTopForBottomLeftControls -= 24;
                    }

                    lastResizePutControlsOverImage = true;
                    TEXT_Mask.Foreground = Brushes.White;
                    rightSideControls.Height = newTopForBottomLeftControls + bottomLeftControls.Height;  // shorten controls to be visible
                }
                else
                {
                    rightSideControls.Height = SEImagesBitmap.Height + (initialRightSideControlsBounds.Height - initialBitmapSize.Height); 
                }

                Canvas.SetTop(bottomLeftControls, newTopForBottomLeftControls);
                Canvas.SetLeft(rightSideControls, SEImagesBitmap.Width + fireLegendGrid.Width);

                // put bottomControls at bottom or for scales > 2 at right of bottomLeftControls
                if (scale > 2)
                {
                    // for some weird reason, alternatingVisibilityTools has an ActualWidth of zero
                    double widthTools = Math.Max(CommonToolsLayer.ActualWidth, DensityLayer.ActualWidth);
                    Canvas.SetLeft(bottomControls, bottomLeftControls.ActualWidth + widthTools);
                    Canvas.SetTop(bottomControls, newTopForBottomLeftControls);
                }
                else
                {
                    Canvas.SetLeft(bottomControls, 0);
                    Canvas.SetTop(bottomControls, newTopForBottomLeftControls + bottomLeftControls.ActualHeight  + 2);
                }

                NudgeWindowToFit();
            }
        }

        base.OnRenderSizeChanged(sizeInfo);
    }


    /// <summary>
    /// Abstracts the issue of determining size, which is complex now that controls may be moved by OnRenderSizeChanged().
    /// </summary>
    /// At scales of 3 or more, the controls are overlaid on the image.
    private Size SizeAtScale(int tryScale)
    {
        double newWidth = (tryScale * initialBitmapSize.Width) + initialRightSideControlsBounds.Width + fireLegendGrid.Width +
            initialWindowExtra.Width;
        double newHeight = (tryScale * initialBitmapSize.Height) + initialWindowExtra.Height;
        if (tryScale < 4)
        {
            // bottomLeftControls are under image
            newHeight += bottomLeftControls.ActualHeight;
            if (tryScale < 3)
            {
                // and other bottom controls stacked in two rows
                newHeight += bottomControls.ActualHeight + 2;
            }
        }

        return new Size(newWidth, newHeight);
    }


    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);

... InitSizesOnceConstructed(); }

the entire XAML file:

<Window 
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' 
    xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:dt="clr-namespace:DrawToolsLib;assembly=DrawToolsLib"
    xmlns:ads="clr-namespace:ADS_Controls;assembly=ADS_UpDownControl"
    xmlns:mc='http://schemas.openxmlformats.org/markup-compatibility/2006' 
    xmlns:d='http://schemas.microsoft.com/expression/blend/2008' 
    mc:Ignorable='d' 
    Height="367" Width="452" Top="2" Left="689"
    Title='Spin-Echo Images' x:Class='Blah.SEImages' 
    Style="{StaticResource windowStyle}" >
    <Window.Resources>
        <Image x:Key="DrawRectangle" Width="16" Height="16" Source="img\DrawRectangle.bmp"/>
        <Image x:Key="DrawRectangleDark" Width="16" Height="16" Source="img\DrawRectangleDark.bmp"/>
        <Image x:Key="DrawOval" Width="16" Height="16" Source="img\DrawOval.bmp"/>
        <Image x:Key="DrawOvalDark" Width="16" Height="16" Source="img\DrawOvalDark.bmp"/>
        <Image x:Key="DrawFree" Width="16" Height="16" Source="img\DrawFree.bmp"/>
        <Image x:Key="DrawFreeDark" Width="16" Height="16" Source="img\DrawFreeDark.bmp"/>
        <Image x:Key="DrawSubQ" Width="16" Height="16" Source="img\DrawSubQ.bmp"/>
        <Image x:Key="DrawSubQDark" Width="16" Height="16" Source="img\DrawSubQDark.bmp"/>
        <Image x:Key="DrawCuts" Width="16" Height="16" Source="img\DrawCuts.bmp"/>
        <Image x:Key="DrawCutsDark" Width="16" Height="16" Source="img\DrawCutsDark.bmp"/>
        <Image x:Key="DrawEdge" Width="16" Height="16" Source="img\DrawEdge.bmp"/>
        <Image x:Key="DrawEdgeDark" Width="16" Height="16" Source="img\DrawEdgeDark.bmp"/>
        <Style TargetType="{x:Type ToggleButton}" x:Key="RectControlStyle">
            <Setter Property="Content" Value="{DynamicResource DrawRectangle}" />
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Content" Value="{DynamicResource DrawRectangleDark}" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type ToggleButton}" x:Key="OvalControlStyle">
            <Setter Property="Content" Value="{DynamicResource DrawOval}" />
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Content" Value="{DynamicResource DrawOvalDark}" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type ToggleButton}" x:Key="FreeControlStyle">
            <Setter Property="Content" Value="{DynamicResource DrawFree}" />
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Content" Value="{DynamicResource DrawFreeDark}" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type ToggleButton}" x:Key="ShowControlStyle">
            <Setter Property="Content" Value="{DynamicResource DrawSubQ}" />
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Content" Value="{DynamicResource DrawSubQDark}" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type ToggleButton}" x:Key="DarkControlStyle">
            <Setter Property="Content" Value="{DynamicResource DrawCuts}" />
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Content" Value="{DynamicResource DrawCutsDark}" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type ToggleButton}" x:Key="Free2ControlStyle">
            <Setter Property="Content" Value="{DynamicResource DrawEdge}" />
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Content" Value="{DynamicResource DrawEdgeDark}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Canvas x:Name="outerCanvas" Margin="0,0,4,4" HorizontalAlignment="Left" VerticalAlignment="Top">
        <Canvas x:Name="BitmapAndToolsOverlay"  Width="305" Height="256" >
            <Image x:Name="SEImagesBitmap"   Margin="0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
            <dt:DrawingCanvasMasking x:Name="drawingCanvas" Background="#00000000"  VerticalAlignment="Top" Width="256" Height="256"/>
        </Canvas>
        <Grid x:Name="fireLegendGrid" Height="256" Width="40" Canvas.Left="305" Canvas.Top="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="17"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="17"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="17"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="17"/>
            </Grid.RowDefinitions>
            <TextBlock x:Name='TEXT_WhiteMark' Grid.Column="0" Grid.Row="0" Text="3499"/>
            <TextBlock x:Name='TEXT_YellowMark' Grid.Column="0" Grid.Row="2" Width='31' Text="2300" />
            <TextBlock x:Name='TEXT_RedMark' Grid.Column="0" Grid.Row="4" Width='31' Text="1100"/>
            <TextBlock x:Name='TEXT_BlackMark' Grid.Column="0" Grid.Row="6" Width='31' Text="0" />
        </Grid>
        <Grid x:Name="rightSideControls" Height="288" Canvas.Left="345" Canvas.Top="0"  >
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="16"/>
                <RowDefinition Height="16"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40"/>
                <ColumnDefinition Width="40"/>
            </Grid.ColumnDefinitions>
            <Slider x:Name='CNTL_WhiteLevel' Grid.Column="0" Grid.Row="0" Maximum='255' Width="24" Minimum='0' Orientation='Vertical' Margin="0,4,0,4"/>
            <Slider x:Name='CNTL_BlackLevel' Grid.Column="1" Grid.Row="0" Maximum='255' Width="24" Minimum='0' Orientation='Vertical'  Margin="0,4,0,4"/>
            <TextBlock x:Name='TEXT_WhiteSetting' Grid.Column="0" Grid.Row="1" Width='37' Height='16' HorizontalAlignment="Stretch" TextAlignment="Center"  Text='1200'/>
            <TextBlock x:Name='TEXT_BlackSetting' Grid.Column="1" Grid.Row="1" Width='37' Height='16' HorizontalAlignment="Stretch" TextAlignment="Center" Text='0'/>
            <TextBlock x:Name='TEXT_Black'  Grid.Column="0" Grid.Row="2" Height='16'  HorizontalAlignment="Center" TextAlignment="Center"><Run Text="Black"/></TextBlock>
            <TextBlock x:Name='TEXT_White'  Grid.Column="1" Grid.Row="2" Height='16'  TextAlignment="Center"><Run Text="White"/></TextBlock>
        </Grid>
        <StackPanel x:Name="bottomLeftControls" Height="27" Canvas.Left="0" Canvas.Top="256" Orientation="Horizontal" Margin="0,4">
            <TextBlock x:Name='TEXT_Mask' Width='Auto' Height='Auto' Margin="4,0" VerticalAlignment="Center"><Run Text="Mask:"/></TextBlock>
            <ComboBox x:Name='CNTL_ROIType' Width='88' Height="21" SelectedIndex="0" Margin="0,0,4,0">
                <ComboBoxItem Content="Analysis"/>
                <ComboBoxItem Content="Phantom"/>
                <ComboBoxItem Content="Background"/>
                <ComboBoxItem Content="Density"/>
            </ComboBox>
            <ComboBox x:Name='CNTL_Operation'  Width='61' Height="21"  SelectedIndex="0" Margin="4,0">
                <ComboBoxItem Content="Add"/>
                <ComboBoxItem Content="Cut"/>
            </ComboBox>
            <Button x:Name='CNTL_Clear' Width='23' Height='23' Margin="4,0,0,0">
                <Image Width="16" Height="16" Source="img\ClearROI.bmp"/>
            </Button>
            <Canvas x:Name="alternatingVisibilityTools" Margin="0,3">
                <StackPanel x:Name="CommonToolsLayer"  Canvas.Top="-1" Canvas.Left="0" Orientation="Horizontal" Width="71" Height="23">
                    <ToggleButton x:Name='CNTL_CommonToolsLayer_Rectangle'  Checked="CNTL_CommonToolsLayer_Rectangle_Click" Style="{StaticResource RectControlStyle}" />
                    <ToggleButton x:Name='CNTL_CommonToolsLayer_Oval' Checked="CNTL_CommonToolsLayer_Oval_Click" Style="{StaticResource OvalControlStyle}" />
                    <ToggleButton x:Name='CNTL_CommonToolsLayer_Free' Checked="CNTL_CommonToolsLayer_Free_Click"  Style="{StaticResource FreeControlStyle}" />
                </StackPanel>
                <StackPanel x:Name="DensityLayer"   Canvas.Top="-1" Canvas.Left="0" Orientation="Horizontal"  Visibility="Hidden" Height="23">
                    <ToggleButton x:Name='CNTL_DensityLayer_Show' Checked="CNTL_DensityLayer_Show_Click" Style="{StaticResource ShowControlStyle}" />
                    <ToggleButton x:Name='CNTL_DensityLayer_Dark' Checked="CNTL_DensityLayer_Dark_Click" Style="{StaticResource DarkControlStyle}" />
                    <ToggleButton x:Name='CNTL_DensityLayer_Free'  Checked="CNTL_DensityLayer_Free_Click" Style="{StaticResource Free2ControlStyle}" />
                </StackPanel>
            </Canvas>
        </StackPanel>
        <StackPanel  x:Name='bottomControls' Orientation="Horizontal" Canvas.Left="0" Canvas.Top="291" Margin="4,0,0,0" Height="34" >
            <Button x:Name='CNTL_Zoom' Width='27' Height="27" Click="CNTL_Zoom_Click">
                <Image Width="21" Height="21" Source="img\DoZoom.bmp"/>
            </Button>
            <ads:ADS_UpDownControl x:Name="CNTL_ImageSwitch" Maximum='7' Minimum='0' Margin="4,0"/>
            <TextBox x:Name='CNTL_ImageNames' MinWidth="180" Height="27" Text="TestCase3A.2_TE06.txt" Margin="4,0"/>
            <Button x:Name='CNTL_ShowHeader' Height="24" Content="Show Header"  Margin="4,0" Padding="4,0"/>
            <Button x:Name='CNTL_SaveROI' Height="24" Content="Save ROI" Margin="4,0" Padding="4,0"/>
        </StackPanel>
    </Canvas>
</Window>


Place the Image control and the stackpanel in a grid and change the horizontal and vertical alignment of the stackpanel to Right and Bottom respectively. I think this should solve the problem.

0

精彩评论

暂无评论...
验证码 换一张
取 消