XAML: Placing controls over an Image - silverlight

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

Related

WPF: Data Binding A Slider Value To A ToolTip Content String In XAML

In a C# based WPF UserControl, I have a set of Sliders to adjust the Red,Green and Blue values of a Color element; essentially the UserControl is a color picker. I have a combination of XAML and C# code behind for the ToolTipOpening Event Handler that allows me to set the individual integer value representing the Color component property value. So, if I have the slider positioned for adjusting the amount of Red in the color in the middle of the Slider, the ToolTip will display "Red: 125", as each Color component property value can range from 0 to 255. The code fragments below for both the XAML and C# work as expected:
XAML:
<UserControl x:Class="CustomColorPicker.ColorPickerUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="colorPicker">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="175" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Label HorizontalAlignment="Right">Red:</Label>
<Slider Grid.Row="0" Grid.Column="1" Name="sliderRed" Minimum="0" Maximum="255"
Margin="{Binding ElementName=colorPicker, Path=Padding}"
Value="{Binding ElementName=colorPicker, Path=Red}"
ToolTipOpening="OnColorSliderToolTipOpening"
ToolTip="Red: 0" />
<Label Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right">Green:</Label>
<Slider Grid.Row="1" Grid.Column="1" Name="sliderGreen" Minimum="0" Maximum="255"
Margin="{Binding ElementName=colorPicker, Path=Padding}"
Value="{Binding ElementName=colorPicker, Path=Green}"
ToolTipOpening="OnColorSliderToolTipOpening" ToolTip="Green: 0" />
<Label Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right">Blue:</Label>
<Slider Grid.Row="2" Grid.Column="1" Name="sliderBlue" Minimum="0" Maximum="255"
Margin="{Binding ElementName=colorPicker, Path=Padding}"
Value="{Binding ElementName=colorPicker, Path=Blue}"
ToolTipOpening="OnColorSliderToolTipOpening" ToolTip="Blue: 0" />
<Rectangle Grid.Column="2" Grid.RowSpan="3"
Margin="{Binding ElementName=colorPicker, Path=Padding}"
Width="85" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding ElementName=colorPicker, Path=Color}" />
</Rectangle.Fill>
</Rectangle>
</Grid>
</UserControl>
C#:
private void OnColorSliderToolTipOpening(object sender, ToolTipEventArgs e)
{
Slider slider = (Slider) sender;
string colorName = "N/A";
int colorValue = 0;
if (slider.Name == "sliderRed")
{
colorName = "Red";
colorValue = (int) Color.R;
}
else if (slider.Name == "sliderGreen")
{
colorName = "Green";
colorValue = (int) Color.G;
}
else if (slider.Name == "sliderBlue")
{
colorName = "Blue";
colorValue = (int) Color.B;
}
string colorTip =
string.Format("{0}: {1}", colorName, colorValue.ToString());
slider.ToolTip = colorTip;
return;
}
Is there a way to eliminate the code behind and set the ToolTip contents to display the same string using the ToolTip.Content and ToolTip.ContentStringFormat properties in XAML only, allowing for the elimination of the ToolTipOpening event handler in the code behind in C#? I've tried the following XAML using DataBinding to format the content string of the Slider's ToolTip:
XAML:
<Slider Grid.Row="2" Grid.Column="1" Name="sliderBlue" Minimum="0" Maximum="255"
Margin="{Binding ElementName=colorPicker, Path=Padding}"
Value="{Binding ElementName=colorPicker, Path=Blue}">
<Slider.ToolTip>
<ToolTip
Content="{Binding ElementName=colorPicker, Path=Blue}"
ContentStringFormat="{}Blue: {0}" />
</Slider.ToolTip>
</Slider>
and the ToolTip Popup display comes up empty:
Binding per ElementName just like RelativeSource with FindAncestor isn't in scope because ToolTip is a Popup which isn't part of the VisualTree (it creates its own). But you can use the ToolTip.PlacementTarget Property to get the Slider control and catch the Value.
<Slider.ToolTip>
<ToolTip Content="{Binding RelativeSource={RelativeSource Self},
Path=PlacementTarget.Value}"
ContentStringFormat="Blue: {0:0}" />
</Slider.ToolTip>
Please note the additional :0 in ContentStringFormat="Blue: {0:0}" to round the Value.

ScrollViewer in TextBlock Template routing the scroll?

i've got a ScrollViewer (name="master"), inside a StackPanel with some example Borders or rectangles and also a TextBox which have more Text than it's displayed, so the TextBox is scrollable. When you scroll the TextBox and came to the top oder bottom border the ScrollViewer which is in the TextBox Template (ScrollViewer x:Name="PART_ContentHost") routed the scrolling to the main ScrollViewer outside ("master").
is it possible to edit the TextBox Template to elimate this behavior?
One way is the combination with another ScrollViewer in the Template, this works fine, but it#s not more possible to see the selection so it's no solution.
Have you any other ideas?
<Style x:Key="TextBoxStyle1" TargetType="{x:Type TextBox}">
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer x:Name="PART_ContentHost" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ScrollViewer Name="master" Height="71" Width="164" HorizontalAlignment="Right" Margin="0,0,38.666,28.833" VerticalAlignment="Bottom">
<StackPanel Orientation="Vertical" Width="121">
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<TextBox Text="TextBox sydxr gs dgh drz h we rths dretghe dtrzuj hwesrtgh
bdnftzh srdztj ser tghbed5rsetzhnrd hserdfgcjmnjs egrfhfn dshgrdxthgj" TextWrapping="Wrap" Height="67" Style="{StaticResource detail_text}" />
<Rectangle Fill="red" Height="45" Stroke="Black"/>
<Rectangle Fill="red" Height="45" Stroke="Black"/>
</StackPanel>
</ScrollViewer>
Thanks a lot.
My suggestion is to capture the PreviewMouseWheelEvent on the textbox itself:
<TextBox Text="TextBox sydxr gs dgh drz h we rths dretghe dtrzuj hwesrtgh
bdnftzh srdztj ser tghbed5rsetzhnrd hserdfgcjmnjs egrfhfn dshgrdxthgj" TextWrapping="Wrap" Height="67" ScrollViewer.VerticalScrollBarVisibility="Auto" PreviewMouseWheel="TextBox_PreviewMouseWheel" />
Note that i included ScrollViewer.VerticalScrollBarVisibility="Auto" also.
Then evaluate if the scroll is being made when either on top or bottom and mark the event as handled if so:
private void TextBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var textbox = sender as TextBox;
var decorator = VisualTreeHelper.GetChild(textbox, 0) as Decorator;
var scrollViewer = decorator.Child as ScrollViewer;
if ((scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight && e.Delta < 0) ||
(scrollViewer.VerticalOffset == 0 && e.Delta > 0))
{
e.Handled = true;
}
}
No need to apply new templates this way, cheers!

How do I access elements that have been dynamically assigned to a control in the form of an XAML resource?

I have the following resource in my window that declares how a certain kind of TabItem should look like.
<Window.Resources>
<StackPanel x:Key="TabSearchContents" x:Shared="False"
Orientation="Vertical">
<Border
BorderThickness="3"
BorderBrush="Purple">
<TextBlock
Text="SEARCH BOOKS"
FontFamily="Verdana"
FontSize="25"
Foreground="Blue"
HorizontalAlignment="Center" />
</Border>
<StackPanel
Height="30"
Orientation="Horizontal"
Margin="5">
<TextBox
x:Name="txtSearch"
Width="650"
FontFamily="Comic Sans MS"
Foreground="Chocolate" />
<Button
x:Name="btnSearch"
Width="100"
Content="Go!"
Click="BtnSearch_Click" />
</StackPanel>
<Grid x:Name="gridResults">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="450"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto">
<ItemsControl x:Name="itmsSearch" ItemsSource="{Binding}" Padding="4"
ItemTemplate="{StaticResource SearchResultItemDT}">
</ItemsControl>
</ScrollViewer>
<StackPanel x:Name="stkpnlDetails">
</StackPanel>
</Grid>
</StackPanel>
</Window.Resources>
Then, in my code-behind, I dynamically create a tab and assign to the TabControl that is already present in my window.
void BtnNewTab_Click(object sender, RoutedEventArgs e)
{
TabItem tb = new TabItem();
tb.Content = this.Resources["TabSearchContents"];
tb.DataContext = _bridge.SearchBooksByTitle("e");
tb.Header = "Wuttp yo!";
Button btnGo = ((Button)tb.FindName("btnSearch"));
ItemsControl i = (ItemsControl)tb.FindName("itmsSearch");
btnGo.Resources.Add("ResultList", i);
daTabs.Items.Add(tb);
tb.Focus();
}
I want to access the btnSearch Button that is declared in my XAML resource.
As it is, this code throws an exception since btnGo turns out to be null (as well as i) since it can't find the expected control via FindName().
I read about the RegisterName() method, but it requires a reference to an instance of the required control... which I don't have.
I dont think you should define your button like this, try defining it in a style, creating a button and assigning the button that style, i think you will be able to get what you are going for this way.
myTheme.xaml
<ResourceDictionary
<Style x:Key="btnSearch" TargetType="{x:Type Button}">
<Setter Property="Width" Value="100"/>
<Setter Property="Content" Value="Go!"/>
<Setter Property="Click" Value="btn_Click"/>
</Style>
ResourceDictionary/>
myCode.cs
Button btnGo = new Button;
btnGo.Style = "{DynamicResource btnSearch}";
Hope this helps,
Eamonn

WPF using ResizeGrip to resize controls

I want that the user can resize a control by draging a resize-grip on the lower right border. With the ResizeGrip there seems to exists the perfect control for achieving this, but I don't see what is the plan to use this control. It does not derive from Thumb (however in msdn is written that it is an "implementation" of it), and does also not support the routed events of Thumb.
What is it correct usage of the ResizeGrip-control.
Update:
I've played around with ResizeGrip and I have experienced a lot of weird problems using it.
The most hard problem was that, using the ResizeGrip in a window that shows also a native ResizeGrip in the bottom right corner (ResizeMode="CanResizeWithGrip"), the window has begun to react really strange on mouse-input. In the end, I have disclaimed to use it.
As a simple alternative, you can use the Thumb-control and attach it an appropriate Template.
Ok, I got bored last night and wrote a little sample for you using Thumb. You should be able to Copy/Paste/Compile/Run.
But basically, I created a UserControl named DialogReplica, just something that looks like a dialog with a grip, you can see it thrown in the main window.
<Window x:Class="ResizeGrip.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ResizeGrip="clr-namespace:ResizeGrip"
Title="MainWindow" Height="350" Width="525">
<Canvas>
<ResizeGrip:DialogReplica Canvas.Top="25" Canvas.Left="100"/>
</Canvas>
Here's the xaml for the UserControl (you mostly interested in the Thumb part):
<UserControl x:Class="ResizeGrip.DialogReplica"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border x:Name="Border" HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="#FF626161" BorderThickness="2" CornerRadius="3">
<DockPanel x:Name="sizableContent" Background="LightGray" Focusable="False" LastChildFill="True" MinHeight="100" MinWidth="100">
<Border DockPanel.Dock="Top" Background="Gray" Height="30">
<DockPanel>
<Button DockPanel.Dock="Right" Width="16" Height="16"
VerticalAlignment="Center" HorizontalAlignment="Center"
VerticalContentAlignment="Top" HorizontalContentAlignment="Center"
Margin="0,0,4,0" Background="Transparent">
<Button.Content>
<Grid>
<Line X1="1" Y1="1" X2="8" Y2="8" Stroke="White" StrokeThickness="1"/>
<Line X1="1" Y1="8" X2="8" Y2="1" Stroke="White" StrokeThickness="1"/>
</Grid>
</Button.Content>
</Button>
<TextBlock Text="Pretend Dialog" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DockPanel>
</Border>
<DockPanel DockPanel.Dock="Bottom" HorizontalAlignment="Stretch">
<Thumb DockPanel.Dock="Right" VerticalAlignment="Bottom" Margin="0,0,1,1"
DragDelta="OnResizeThumbDragDelta"
DragStarted="OnResizeThumbDragStarted"
DragCompleted="OnResizeThumbDragCompleted">
<Thumb.Style>
<Style TargetType="{x:Type Thumb}" BasedOn="{x:Null}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="resizeVisual" DockPanel.Dock="Right" VerticalAlignment="Bottom" >
<Line X1="6" Y1="18" X2="18" Y2="6" Stroke="DarkGray" StrokeThickness="1.5"/>
<!--smallest/right|bottom most -->
<Line X1="10" Y1="18" X2="18" Y2="10" Stroke="DarkGray" StrokeThickness="1.5"/>
<Line X1="14" Y1="18" X2="18" Y2="14" Stroke="DarkGray" StrokeThickness="1.5"/>
<!--longers/left|top most-->
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="SizeNWSE"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</Thumb.Style>
</Thumb>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Margin="12" Width="75" TabIndex="1" Content="Ok"/>
</StackPanel>
</DockPanel>
<StackPanel HorizontalAlignment="Center" Margin="16,16,16,4">
<TextBlock Text="Drag the lower right corner to resize."/>
</StackPanel>
</DockPanel>
</Border>
finally, here's the code behind for the UserControl
public partial class DialogReplica : UserControl
{
private Cursor _cursor;
public DialogReplica()
{
InitializeComponent();
}
private void OnResizeThumbDragStarted(object sender, DragStartedEventArgs e)
{
_cursor = Cursor;
Cursor = Cursors.SizeNWSE;
}
private void OnResizeThumbDragCompleted(object sender, DragCompletedEventArgs e)
{
Cursor = _cursor;
}
private void OnResizeThumbDragDelta(object sender, DragDeltaEventArgs e)
{
double yAdjust = sizableContent.Height + e.VerticalChange;
double xAdjust = sizableContent.Width + e.HorizontalChange;
//make sure not to resize to negative width or heigth
xAdjust = (sizableContent.ActualWidth + xAdjust) > sizableContent.MinWidth ? xAdjust : sizableContent.MinWidth;
yAdjust = (sizableContent.ActualHeight + yAdjust) > sizableContent.MinHeight ? yAdjust : sizableContent.MinHeight;
sizableContent.Width = xAdjust;
sizableContent.Height = yAdjust;
}
}

Silverlight: Applying datacontext to an element within a style

I have defined a style in app.xaml. This style contains several text TextBlocks which I would like to controle as I apply the style to an object, in this case a UserPin.
How can I access these TextBlocks runtime?
I get the style by:
Style = Application.Current.Resources["UserPin"] as Style;
The style looks like this:
<Style x:Name="UserPin" TargetType="RRML_UserControls:UserPin" >
<Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
<Setter Property="AnchorPoint" Value="0.5,0.5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RRML_UserControls:UserPin">
<Grid Height="71.969" Width="Auto">
<Grid.RenderTransform>
<ScaleTransform x:Name="PART_PinScale" />
</Grid.RenderTransform>
<Grid.RowDefinitions>
<RowDefinition Height="29"/>
<RowDefinition Height="16"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.247*"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="0.753*"/>
</Grid.ColumnDefinitions>
<Image Height="Auto" Source="Resources/Users.png" x:Name="PART_imgUser" VerticalAlignment="Top" Stretch="Uniform" Margin="0,0,0,0" Grid.Column="1">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,0" Width="Auto" Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Center" TextAlignment="Center" x:Name="txtBottom" Text="{Binding Mode=OneWay, Path=LocationName}">
<TextBlock.DataContext>
<RRML_RRMLServiceReference:Location LocationName="Initial Name"/>
</TextBlock.DataContext>
</TextBlock>
<TextBlock HorizontalAlignment="Right" Margin="0,0,0,0" VerticalAlignment="Center" Text="L" TextWrapping="Wrap"/>
<TextBlock Margin="0,0,0,0" Text="R" TextWrapping="Wrap" d:LayoutOverrides="Width, Height" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The TextBlock value I'm trying to set is txtBottom.
As you can see I have tried to apply a datacontext and a databinding to the field. This works, but all objects get the value "Initial Name" of course.
My questions are:
how can I apply my datacontext so txtBottom.Text changes, or
how can I change the value of the TextBlock named txtBottom without databinding?
in short can I access these fields or properties at all?
Runtime :)
So far I have found that Triggers may be used only in WPF.
I think of something like this:
var styledobject = new NiceObject();
styledobject.Style = Application.Current.Resources["UserPin"] as Style;
styledobject.DataContext = locationData;
Where locationData is my object containing data.
If anyone wonders; I am placing icons on a map and want to name them.
You should not explicitly apply DataContext on the TextBlock. DataContext is inherited by child FrameworkElements. You should try to set data context explicitly as little and as high up the Visual Tree as possible (for your own sanity's sake :-))
If this is a custom control, you can override on the OnApplyTemplate method and use the GetTemplateChild(string name) to retrieve references to named elements within your control.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
TextBlock txtBottom = GetTemplateChild("txtBottom") as TextBlock;
}
Externally, if you must, you can imperatively access that specific control at runtime using an extension method to traverse the Visual Tree to find it by name.
public static T FindChild<T>(this DependencyObject element, string name)
where T : FrameworkElement
{
//Code to find the control
}

Resources