I need to rotate the UI 180 degrees of my surface application in runtime when a user presses a button. How do I do this?
Just apply a RotateTransform on your topmost panel (I think you can even do it on the actual surface window if you want) with an angle of 180 degrees.
<s:SurfaceWindow x:Class="SurfaceApplication1.SurfaceWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.microsoft.com/surface/2008"
Title="SurfaceApplication1">
<Grid>
<Grid.LayoutTransform>
<RotateTransform x:Name="mainOrientation"/>
</Grid.LayoutTransform>
<s:SurfaceButton Click="btn_Click" Content="Click to rotate" />
... other content here ...
</Grid>
</s:SurfaceWindow>
And in code behind:
private void btn_Click (object sender, RoutedEventArgs e)
{
if (mainOrientation.Angle == 0)
mainOrientation.Angle = 180;
else
mainOrientation.Angle = 0;
}
As a related topic, you can also listen to the surface's OrientationChanged event to know when a user has changed side of your app. A good practice is to flip the app to the correct side when this happens.
Related
the case is this one:
I have an image representing a schema, let's say a cross like the following
I need to include the image in a WPF UserControl and let the user click on each of the branches (red, green or blue...) and, according to the branch selected, do something different.
What would be the best way to solve this?
I tried with canvas but I don't find a way to trace correctly the background image with shapes (also because the real image is not so simple as the sample cross here)
thanks for any suggestion
It depends on the comlexity of shapes but it is not difficult to find the shape where mouse up event is fired by VisualTreeHelper.HitTest method.
Let's say there is a Canvas which has two Rectangles inside.
<Canvas Background="Transparent"
PreviewMouseUp="Canvas_PreviewMouseUp">
<Rectangle x:Name="Rect1" Canvas.Left="20" Canvas.Top="20"
Width="20" Height="20" Fill="Red"/>
<Rectangle x:Name="Rect2" Canvas.Left="80" Canvas.Top="80"
Width="20" Height="20" Fill="Green"/>
</Canvas>
Catching its PreviewMouseUp event, you can tell the Rectangle where that event is fired.
private void Canvas_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
var position = e.GetPosition((IInputElement)sender);
var result = VisualTreeHelper.HitTest((Visual)sender, position);
if (result.VisualHit is Rectangle rect)
{
Debug.WriteLine($"Hit {rect.Name}");
}
}
How to force the layout measurements update?
I have simplified layout I am problem with; when you click the button first time you get one measurement and on the second click different one.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var w = mywindow.ActualWidth;
gridx.Width = w;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
btn3.Width = 100;
var w = mywindow.ActualWidth;
gridx.Width = w - btn3.Width;
InvalidateArrange();
InvalidateMeasure();
MessageBox.Show(btn1.ActualWidth.ToString());
}
Window
<Window x:Class="resizet.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" Name="mywindow">
<DockPanel HorizontalAlignment="Stretch" LastChildFill="False">
<Grid HorizontalAlignment="Stretch" DockPanel.Dock="Left" Name="gridx">
<Button HorizontalAlignment="Stretch" Content="btn in grid" Click="Button_Click" />
</Grid>
<Button Name="btn2" Content="btn2" Width="0" DockPanel.Dock="Right" HorizontalAlignment="Left"></Button>
</DockPanel>
</Window>
This fixes the problem:
btn3.Width = 100;
btn3.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
var w = mywindow.ActualWidth;
gridx.Width = w - btn3.Width;
with additional
private static Action EmptyDelegate = delegate() { };
Changing the Width property must invalidate the layout on its own, you don't need to call InvalidateXXX() yourself.
The catch is that the layout is not updated immediately, but on the next iteration of the message loop. So the ActualWidth will not be changed immediately.
If you want the Grid to resize automatically when the button width is increasing, why not use the layout management and put the both into different columns of an outer Grid?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid x:Name="gridx"
Grid.Column="0">
<Button HorizontalAlignment="Stretch"
Click="Button_Click"/>
</Grid>
<Button x:Name="btn2"
Content="btn2"
Width="0"
Grid.Column="1"/>
</Grid>
And in code-behind
private void Button_Click(object sender, RoutedEventArgs e)
{
btn2.Width = 100;
}
In a strict sense, #Daniel has provided some code that fixes the problem posed by the question. But the result is rather bad, putting layouting code into an event handler. The grid and the button might look good after the button got pressed, but once the user makes the window size bigger, the grid will not grow and will not use the available size. The user would have to press the button again to make the grid grow. That's most likely not how things should be and that's why #Vlad's answer is better.
WPF uses just one thread to process events and layouting, but they get executed in different phases. If width gets changed, the MeasureDirty flag of the control gets set, then the processing of the event continues immediately. Once this event and all other events needing processing are completed, only then starts WPF with the layouting (i.e. measure, arrange, render). Here is an overview how that works:
For a detailed description what happens in every step, see my article on CodeProject Deep Dive into WPF Layouting and Rendering
btn3.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
This statement halts the execution of the button event handler and forces a whole layouting / render phase to run, only then continues with the event handler code to change the width of the grid, which will force another layouting / render cycle to run.
Recommendations:
Do not set Height and Width in event handlers, unless you want them to be fixed and not to change, even the available space changes.
Use WPF controls like Grid, etc. to make best use of the available space
If you cannot find a WPF control like Grid which matches your layouting needs, write your own Control and put all the layouting code into MeasureOverride() and ArrangeOverride().
I have the following control:
<UserControl x:Class="FooBar.AnnotationControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="500" >
<ScrollViewer Height="400" Width="500">
<Canvas Height="400" Width="500" Name="ctlCanvas" MouseLeftButtonDown="MouseLeftButtonDownHandler" MouseWheel="Canvas_MouseWheel" >
<Canvas.RenderTransform>
<ScaleTransform x:Name="ZoomTransform" />
</Canvas.RenderTransform>
</Canvas>
</ScrollViewer>
</UserControl>
namespace FooBar
{
public partial class AnnotationControl : UserControl
{
public AnnotationControl()
{
InitializeComponent();
}
private void MouseLeftButtonDownHandler( object sender, MouseButtonEventArgs args)
{
//Do Something
}
private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
ctlCanvas.Measure(new Size(ctlCanvas.ActualWidth * ZoomTransform.ScaleX, ctlCanvas.ActualHeight * ZoomTransform.ScaleY));
}
}
}
I'm trying to get the scroll viewer to respond to the scaling of the Canvas. The call to Canvas.Measure doesn't appear to change the Desired size of the Canvas. Any idea what is going on here?
You should NOT call Measure on your own. This method is supposed to be called in the layout step, and not somewhere else. Also a RenderTransform doesn't change your Size. The RenderTransform is applied AFTER the actual Layout is done. So you have a scrollviewer that don't need to scroll its content, because its the same size. What you might want is LayoutTransform.
Canvas is the most primitive element and it simply not designed to work with the ScrollViewer. Use Grid/StackPanel/WarPanel/UniformGrid instead.
Ok, I seem to have found a solution. It looks like I can wrap my canvas with another canvas and when I scale it, I simply set the height and width for the outer canvas = initial height and width times the current X and Y scales of the ScaleTransform.
I am trying to implement a Dots and Boxes style game in Silverlight for Windows Phone 7. What is the best way to draw the dot and box grid so that I get notified when someone touches the space between two boxes? What XAML elements should I look at using?
A polygon shaped like the image below, with an overlaid line, would be your best bet.
You will set the polygon fill (shown in blue) to 1% alpha so that it is not visible, but is hit-testable (0% alpha turns off hit testing).
If you create one as a usercontrol, you can simply place them around your grid of dots with 90% rotation on the vertical ones:
The dots can be simple ellipses (turn off isHitTestVisible on these):
You can then simply turn on/off the visibility of the lines in the user controls (which are always present for hit-testing):
I suggest a canvas for the outer control to give you fine position adjustment from code, but a grid will work too if you get the margin offsets right.
Usercontrol XAML (created with Blend):
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="DotsAndBoxes.Connector"
d:DesignWidth="280" d:DesignHeight="80">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Path Data="M27.706778,279.98367 L68.48111,239.30304 L266.99857,239.30304 L306.88052,278.89899 L266.99857,318.49493 L68.481102,318.49493 z"
Fill="#022E2EFB"
Stretch="Fill"
UseLayoutRounding="False"
IsHitTestVisible="True"
MouseLeftButtonDown="Path_MouseLeftButtonDown"/>
<Path Data="M0,40 L40.218182,40 L280,40" Height="5" Stretch="Fill" StrokeThickness="5" UseLayoutRounding="False" VerticalAlignment="Center" Stroke="White" IsHitTestVisible="False"/>
</Grid>
</UserControl>
Expose a "click" event on the User Control, that is called from a LeftMouseButtonDown event on the polygon and catch those click events in the high-level container:
namespace DotsAndBoxes
{
public partial class Connector : UserControl
{
public event EventHandler<System.Windows.Input.MouseButtonEventArgs> Clicked;
public Connector()
{
// Required to initialize variables
InitializeComponent();
}
private void Path_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (Clicked != null)
{
Clicked(this, e);
}
}
}
}
You could generate one of these polygons by hand as the coordinates required are quite simple.
i want to do a screen capture of a running silverlight 3 application, from within the app, and then i want to present this to the user as a thumbnail, say in an Image control.
am i dreaming?
For a simple page:
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
<Ellipse Fill="Red" Width="100" Height="100"></Ellipse>
<Button x:Name="btnCapture" Click="btnCapture_Click" Width="30" Height="25"></Button>
<Image x:Name="imgThumbnail" Width="50" Height="50"></Image>
</StackPanel>
</Grid>
with the event handler:
private void btnCapture_Click(object sender, RoutedEventArgs e)
{
WriteableBitmap bmp = new WriteableBitmap(LayoutRoot, null);
this.imgThumbnail.Source = bmp;
}
You are dreaming if you want to do a true screen capture (outside the plugin).
The WriteableBitmap answer is correct if you just want to capture a partial or complete visual tree rendering of the Silverlight app only.