XAML:将控件放在图像上

时间:2011-05-22 09:33:33

标签: silverlight xaml

我需要在Image单元格中显示Grid。在图像的顶部,必须在图像的右下角添加StackPanel个控件(例如缩放,亮度等)。如何才能做到这一点。我正在执行以下操作,但不确定如何将StackPanel控件放在图像的右下角。即使用户调整浏览器窗口大小,也需要保持位置。

<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>

3 个答案:

答案 0 :(得分:3)

用户界面的目的有点不清楚,所以我暂时不再提供更完整的答案。但是,如果您只是想要一个图像,并且想要使用控件覆盖此图像的右下角,那么Grid就是解决方案: -

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

此网格的大小将与图像的大小无关(除非控件面板越小),控制面板将浮动在图像的右下角顶部。这种情况越少,就越让组件完成工作。

由于您的某个控件是“缩放”,我怀疑您还有其他问题要解决,这可能最终使这个问题没有实际意义,但上述内容是您现在所需要的本质。

答案 1 :(得分:0)

我通过覆盖在WPF中解决了类似的问题 protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)

当用户放大屏幕时我需要随意移动,所以我可以调整图像大小,保持宽高比,并为3倍和4倍变焦腾出空间,不得不移动控制,然后在图像上叠加一些。

我通过在其中嵌套了一个最外面的Canvas元素来实现这一点,它可以处理大部分布局以及需要在StackPanels中移动或覆盖的控件集。

请注意我是如何通过从初始布局驱动逻辑来避免过多的硬编码 - 如果你在XAML中稍微重新安排一些东西它应该通过调整相对于几个关键对象的东西来应对。

    /**
     * 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();         }

整个XAML文件:

<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>

答案 2 :(得分:0)

将Image控件和stackpanel放在网格中,并将stackpanel的水平和垂直对齐方式分别更改为Right和Bottom。我认为这应该可以解决问题。