I'm writing a window program in Silverlight, meaning that the top bar of a popup has a draggable area, and within that draggable area, an "X" that closes the window. My drag function has a capturemouse() event which, when combined with the event bubbling that occurs, prevents the close function from being called. Here's the code:
private void close_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e){
pop.IsOpen = false;
hasFocus = true;
}
private void topBar_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Border item = sender as Border;
mouseY = e.GetPosition(null).Y;
mouseX = e.GetPosition(null).X;
draggable = true;
item.CaptureMouse();
}
private void topBar_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if(draggable){
double changeY = e.GetPosition(null).Y - mouseY;
double changeX = e.GetPosition(null).X - mouseX;
double top = changeY + (double)pop.GetValue(Canvas.TopProperty);
double left = changeX + (double)pop.GetValue(Canvas.LeftProperty);
if(top<0){
top = 0;
}
if(left<0){
left = 0;
}
if(left>670){
left = 670;
}
if(top>450){
top = 450;
}
pop.SetValue(Canvas.TopProperty, top);
pop.SetValue(Canvas.LeftProperty, left);
mouseY = e.GetPosition(null).Y;
mouseX = e.GetPosition(null).X;
}
}
private void topBar_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Border item = sender as Border;
draggable = false;
mouseY = -1;
mouseX = -1;
item.ReleaseMouseCapture();
}
EDIT: here is the XAML for the entire popup:
<Popup x:Name="pop" Height="200" Width="200" VerticalAlignment="Center" HorizontalAlignment="Center">
<Border CornerRadius="5" Width="200" Height="200" Background="#FFFAFCFF" BorderThickness="1">
<Border.BorderBrush>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#99666666" Offset="0" />
<GradientStop Color="#99F5F5F5" Offset="0.5"/>
<GradientStop Color="#99666666" Offset="1"/>
</LinearGradientBrush>
</Border.BorderBrush>
<StackPanel>
<Border x:Name="topBar" CornerRadius="4,4,0,0" BorderBrush="Silver" BorderThickness="0,0,0,1" Background="Crimson" Width="198" Height="20" MouseLeftButtonDown="topBar_MouseLeftButtonDown" MouseMove="topBar_MouseMove" MouseLeftButtonUp="topBar_MouseLeftButtonUp">
<Image x:Name="close" Source="X.png" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,7,0" Height="11" Width="11" MouseLeftButtonUp="close_MouseLeftButtonUp" />
</Border>
<StackPanel Margin="10">
<TextBlock Text="Printer info goes here..." />
</StackPanel>
</StackPanel>
</Border>
</Popup>
The problem occurs because of your MouseCapture calls. When you set the mousecapture, the border is the only control that will be allowed to initiate mouse events. This means the image, while the mouse button is down, no longer triggers mouseevents.
Without the mousecapture it should work fine. Just for my curiosity, why do you set and release it?
I hope this helps.
Edit:
You can get the position of the mouseEvent and see if it falls in the image:
var x = e.GetPosition(close).X;
var y = e.GetPosition(close).Y;
if (0 <= x && x <= 11 && 0 <= y && y <= 11)
{
//do the close call
}
I haven't tested this code but it should be close to what you want to do.
Related
I have a UserControl in which I create an Style that is applied later to the ContentControl of a Popup (below is all defined in the UserControl):
<Style x:Key="ttPopupContent" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<!-- BOTTOM Popup -->
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid x:Name="Grid">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle MinWidth="40" Fill="#fff" Stroke="#FF000000" Grid.Row="1" />
<StackPanel Grid.Row="1">
<TextBlock Text="My popup title for bottom popup" TextWrapping="Wrap"/>
<TextBlock Text="My popup body content for bottom popup" TextWrapping="Wrap" />
</StackPanel>
<Path Fill="#fff" Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left"
Margin="{TemplateBinding Tag}" Width="20" Grid.Row="0" Data="M 0,21 L 10,0 20,21" />
<ContentPresenter Margin="8" Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Placement, ElementName=myPopup}" Value="Top">
<!-- TOP Popup -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid x:Name="Grid">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Rectangle MinWidth="40" Fill="#fff" Stroke="#FF000000" Grid.Row="0" />
<StackPanel Grid.Row="0">
<TextBlock Text="My popup title for top popup"
TextWrapping="Wrap"/>
<TextBlock Text="My popup body content for top popup"
TextWrapping="Wrap" />
</StackPanel>
<Path Fill="#fff" Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left"
Margin="{TemplateBinding Tag}" Width="20" Grid.Row="1" Data="M 0,0 L 10,20 20,0" />
<ContentPresenter Margin="8" Grid.Row="0" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- this label is placed at leftmost of the screen -->
<TextBlock x:Name="txtBlck1" Text="ShowInfo" />
<Popup x:Name="myPopup1" AllowsTransparency="True" Opened="Popup_Opened1"
PlacementTarget="txtBlck1" Placement="Bottom">
<ContentControl Style="{StaticResource ttPopupContent}"/>
</Popup>
<!-- this label is placed at rightmost of the screen -->
<TextBlock x:Name="txtBlck2" Text="AnotherLabel" />
<Popup x:Name="myPopup2" AllowsTransparency="True" Opened="Popup_Opened2"
PlacementTarget="txtBlck2" Placement="Bottom">
<ContentControl Style="{StaticResource ttPopupContent}"/>
</Popup>
Sometimes it does not put the arrow pointing correctly to the label at which popup is bound. For example, if I hover on the mouse on label "AnotherLabel" it is drawn as follows (this label is at the rightmost of the screen):
as you can see the arrow is not placed in the right place. However, I have another label "ShowInfo" that is placed at the leftmost of the screen, then it works:
So I am trying to adjust the arrow horizontal alignment to point correctly to the label by doing this in code-behind (xaml.cs):
private void Popup_Opened1(object sender, EventArgs e)
{
UIElement target = myPopup1.PlacementTarget;
Point adjust = target.TranslatePoint(new Point(8, 0), popup);
if (adjust.Y > 0)
{
popup.Placement = System.Windows.Controls.Primitives.PlacementMode.Top;
}
myPopup1.Tag = new Thickness(adjust.X, -1.5, 0, -1.5);
}
private void Popup_Opened2(object sender, EventArgs e)
{
UIElement target = myPopup2.PlacementTarget;
Point adjust = target.TranslatePoint(new Point(8, 0), popup);
if (adjust.Y > 0)
{
popup.Placement = System.Windows.Controls.Primitives.PlacementMode.Top;
}
myPopup2.Tag = new Thickness(adjust.X, -1.5, 0, -1.5);
}
What I am trying to do in code-behind is put the arrow in the correct place by adjusting it horizontally as explained here (in that case is a tooltip, in my case is a Popup).
I have two problems:
Adjusting horizontally the arrow to point correctly to the label.
The arrow is not drawn correctly, it appears a black line under. See below image:
You should extend Popup to conveniently implement the desired behavior.
I suggest to internally set the Popup.Placement to PlacementMode.Custom to enable custom positioning.
This gives you easy access to the positioning context in order to detect when the placement target is positioned right on the screen (in other words the Popup is right aligned with the parent Window.
The arrow box and the tool tip content should be hosted by a custom control.
To draw the arrow box properly you should create and draw geometry for example by overriding UIElement.OnRender of the custom control.
For simplicity, the following example only supports bottom right placement. You can follow the simple pattern to extend the features to support additional positioning (read comments in code):
public class MyToolTip : Popup
{
internal enum Side
{
None = 0,
Left,
Top,
Right,
Bottom
}
private MyToolTipContent ToolTipContent { get; }
static MyToolTip()
{
PlacementTargetProperty.OverrideMetadata(typeof(MyToolTip), new FrameworkPropertyMetadata(default(object), OnPlacementTargetChanged));
PlacementProperty.OverrideMetadata(typeof(MyToolTip), new FrameworkPropertyMetadata(null, CoercePlacement));
}
public MyToolTip()
{
this.ToolTipContent = new MyToolTipContent();
this.Child = this.ToolTipContent;
// Enable custom placement to get context related placement info
// like size and positions.
this.CustomPopupPlacementCallback = CalculatePosition;
this.Placement = PlacementMode.Custom;
this.AllowsTransparency = true;
}
private static void OnPlacementTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as MyToolTip).OnPlacementTargetChanged(e.OldValue as UIElement, e.NewValue as UIElement);
private static object CoercePlacement(DependencyObject d, object baseValue)
{
// Backup/delegate original PlacementMode as we need to override it
// in order to enable custom placement (and to keep it enabled by force).
(d as MyToolTip).ToolTipContent.Placement = (PlacementMode)baseValue;
// Enforce custom placement (invokation of the Popup.CustomPopupPlacementCallback)
return PlacementMode.Custom;
}
// Show Popup on mouse hover over the placement target
protected virtual void OnPlacementTargetChanged(UIElement oldTarget, UIElement newTarget)
{
if (oldTarget is not null)
{
newTarget.MouseEnter -= OnTargetMouseEnter;
newTarget.MouseLeave -= OnTargetMouseLeave;
}
if (newTarget is not null)
{
newTarget.MouseEnter += OnTargetMouseEnter;
newTarget.MouseLeave += OnTargetMouseLeave;
}
}
private void OnTargetMouseLeave(object sender, MouseEventArgs e) => this.IsOpen = false;
private void OnTargetMouseEnter(object sender, MouseEventArgs e) => this.IsOpen = true;
private CustomPopupPlacement[] CalculatePosition(Size popupSize, Size targetSize, Point offset)
{
Side clippingSide = GetParentWindowClippingSide(targetSize);
// TODO::Handle clipping of remaining sides (top and bottom. Left side will always fit).
switch (clippingSide)
{
// Popup will clip right Window bounds => shift Popup to the left to compensate
// (i.e. right align with parent Window).
case Side.Right:
AlignRightSide(popupSize, ref offset, targetSize);
break;
}
// TODO::Handle remaining modes
switch (this.ToolTipContent.Placement)
{
case PlacementMode.Bottom:
offset.Offset(0, targetSize.Height);
break;
case PlacementMode.Top:
break;
}
// Enforce OnRender to update the visual with the latest instructions
this.ToolTipContent.InvalidateVisual();
return new[] { new CustomPopupPlacement(offset, PopupPrimaryAxis.None) };
}
private void AlignRightSide(Size popupSize, ref Point offset, Size targetSize)
{
offset = new Point(targetSize.Width, 0);
offset.Offset(-popupSize.Width, 0);
this.ToolTipContent.HorizontalToolTipArrowAlignment = HorizontalAlignment.Right;
}
private Side GetParentWindowClippingSide(Size targetSize)
{
Window parentWindow = Window.GetWindow(this.PlacementTarget);
var parentWindowBounds = new Rect(new Point(), parentWindow.RenderSize);
Point targetPositionInParentWindow = this.PlacementTarget.TranslatePoint(new Point(), parentWindow);
var targetBounds = new Rect(targetPositionInParentWindow, targetSize);
/* Check for clipping */
bool isTargetRightSideInParentWindowBounds = parentWindowBounds.Contains(targetBounds.TopRight);
if (!isTargetRightSideInParentWindowBounds)
{
return Side.Right;
}
// TODO::Check if Popup clips top or bottom Window bounds following the above pattern. If it does, switch to Bottom/Top placement
// to make it fit the parent's bounds by changing the OriginalPlacementMode accordingly.
// If you don't want to overwrite the original value introduce a dedicated property
// that you really depend on (for example OriginalPlacementModeInternal).
// Popup completely fits the parent using the desired placement.
return Side.None;
}
}
MyToolTipContent.cs
The custom control to make up the actual content of the Popup, responsible for drawing the visual geometries.
internal class MyToolTipContent : Control
{
public PlacementMode Placement
{
get => (PlacementMode)GetValue(PlacementProperty);
set => SetValue(PlacementProperty, value);
}
public static readonly DependencyProperty PlacementProperty = DependencyProperty.Register(
"Placement",
typeof(PlacementMode),
typeof(MyToolTipContent),
new PropertyMetadata(default));
private const double ArrowHeight = 12;
private const double ArrowWidth = 12;
internal HorizontalAlignment HorizontalToolTipArrowAlignment { get; set; }
static MyToolTipContent()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyToolTipContent), new FrameworkPropertyMetadata(typeof(MyToolTipContent)));
}
protected override void OnRender(DrawingContext drawingContext)
{
SolidColorBrush fillBrush = Brushes.Transparent;
var strokePen = new Pen(Brushes.Black, 2);
// Make room for the stroke
this.Margin = new Thickness(strokePen.Thickness);
var toolTipGeometry = new PathGeometry();
/* Create geometry depending on tool tip position.
*
* TODO::Draw different geometries based on different placement.
*
*/
if (this.HorizontalToolTipArrowAlignment == HorizontalAlignment.Right
&& this.Placement == PlacementMode.Bottom)
{
// "Push" content down to make room for the arrow
this.Padding = new Thickness(0, ArrowHeight, 0, 0);
var arrowBoxLines = new PolyLineSegment(new[]
{
new Point(this.RenderSize.Width - ArrowWidth, ArrowHeight),
new Point(this.RenderSize.Width - ArrowWidth / 2, 0),
new Point(this.RenderSize.Width, ArrowHeight),
new Point(this.RenderSize.Width, this.RenderSize.Height),
new Point(0, this.RenderSize.Height),
}, true);
var arrowBoxPath = new PathFigure(
new Point(0, ArrowHeight),
new[] { arrowBoxLines },
true);
toolTipGeometry.Figures.Add(arrowBoxPath);
}
drawingContext.DrawGeometry(fillBrush, strokePen, toolTipGeometry);
base.OnRender(drawingContext);
}
}
Generic.xaml
<Style TargetType="{x:Type local:MyToolTipContent}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<Border Padding="{TemplateBinding Padding}">
<StackPanel>
<TextBlock Text="My popup title for bottom popup"
TextWrapping="Wrap" />
<TextBlock Text="My popup body content for bottom popup"
TextWrapping="Wrap" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am trying to make a simple 2D Game in WPF, and I've come across a problem I can't solve.
Let's say I have a player on a 700x700 Canvas, but my MainWindow's Width and Height are set to 400.
I want to be able to have a camera like feature, that follows a rectangle object on the canvas (this object symbolises the player) and shows the corresponding portion of the canvas whenever the player moves.
In theory how could I implement a feature like this?
Here's a rough sample of how to do that with a ScrollViewer. Use the arrow keys to move the player around while keeping it in the view of the "camera". The canvas' background is set to a radial-brush, to see the camera moving.
MainWindow.xaml
<Window x:Class="WpfApp6.MainWindow"
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"
Title="MainWindow"
Background="#222"
Loaded="Window_Loaded"
SizeToContent="WidthAndHeight"
PreviewKeyDown="Window_PreviewKeyDown">
<Grid>
<ScrollViewer x:Name="CanvasViewer"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden">
<Canvas x:Name="Canvas"
IsHitTestVisible="False">
<Canvas.Background>
<RadialGradientBrush>
<GradientStop Offset="0"
Color="Orange" />
<GradientStop Offset="1"
Color="Blue" />
</RadialGradientBrush>
</Canvas.Background>
</Canvas>
</ScrollViewer>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
double _playerSize;
Rectangle _playerRect;
Vector _playerPosition;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
InitializeSizes();
InitializePlayerRect();
}
#region initialize
private void InitializeSizes()
{
_playerSize = 50;
Canvas.Width = 700;
Canvas.Height = 700;
CanvasViewer.Width = 400;
CanvasViewer.Height = 400;
}
private void InitializePlayerRect()
{
_playerRect = new Rectangle
{
Fill = Brushes.Lime,
Width = _playerSize,
Height = _playerSize,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top
};
Canvas.Children.Add(_playerRect);
}
#endregion
#region move player
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Left: MovePlayerLeft(); break;
case Key.Up: MovePlayerUp(); break;
case Key.Right: MovePlayerRight(); break;
case Key.Down: MovePlayerDown(); break;
}
}
private void MovePlayerLeft()
{
var newX = _playerPosition.X - _playerSize;
_playerPosition.X = Math.Max(0, newX);
UpdatePlayerPositionAndCamera();
}
private void MovePlayerUp()
{
var newY = _playerPosition.Y - _playerSize;
_playerPosition.Y = Math.Max(0, newY);
UpdatePlayerPositionAndCamera();
}
private void MovePlayerRight()
{
var newX = _playerPosition.X + _playerSize;
_playerPosition.X = Math.Min(Canvas.Width - _playerSize, newX);
UpdatePlayerPositionAndCamera();
}
private void MovePlayerDown()
{
var newY = _playerPosition.Y + _playerSize;
_playerPosition.Y = Math.Min(Canvas.Height - _playerSize, newY);
UpdatePlayerPositionAndCamera();
}
#endregion
#region update player and camera
private void UpdatePlayerPositionAndCamera()
{
UpdatePlayerPosition();
UpdateCamera();
}
private void UpdatePlayerPosition()
{
// move the playerRect to it's new position
_playerRect.Margin = new Thickness(_playerPosition.X, _playerPosition.Y, 0, 0);
}
private void UpdateCamera()
{
// calculate offset of scrollViewer, relative to actual position of the player
var offsetX = _playerPosition.X / 2;
var offsetY = _playerPosition.Y / 2;
// move the "camera"
CanvasViewer.ScrollToHorizontalOffset(offsetX);
CanvasViewer.ScrollToVerticalOffset(offsetY);
}
#endregion
}
WPF isn't really the right technology for games, you're much better off using something like MonoGame.
To answer your question, though, you can wrap your canvas in a ScrollViewer:
<ScrollViewer x:Name="theScrollView" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" CanContentScroll="True">
<Canvas x:Name="theCanvas" Width="5000" Height="5000" />
</ScrollViewer>
Then in code you scroll the view to whatever position your camera is at:
theScrollView.ScrollToHorizontalOffset(100);
I have a Canvas and a ToolBarPanel inside it.
I want to resize the ToolBarPanel using a Thumb, so I placed the Thumb inside the ToolBarPanel. When I catch the DragDelta of the Thumb, the width is not changed. Below is the code:
<Canvas x:Uid="userControlCanvas" Name="userControlCanvas" Width="300" Height="300">
<ToolBarPanel x:Uid="categoriesToolBar" Name="categoriesToolBar" Visibility="Visible"
Height="300" Width="10" Panel.ZIndex="100"
Background="#CCDCDCDC"
SizeChanged="toolbarSizeChanged">
<Thumb x:Name="splitPane" Cursor="SizeWE" Height="300" Canvas.Left="2" Width="8" DragStarted="splitPaneDragStarted" DragDelta="splitPaneDragging">
</Thumb>
</ToolBarPanel>
</Canvas>
And the cs code:
private void splitPaneDragging(object sender, DragDeltaEventArgs e) {
categoriesToolBar.Width = categoriesToolBar.ActualWidth + e.HorizontalChange;
//categoriesToolBar.InvalidateMeasure();
categoriesToolBar.UpdateLayout();
//categoriesToolBar.InvalidateArrange();
//categoriesToolBar.InvalidateVisual();
//categoriesToolBar.InvalidateMeasure();
//Width is always the initial width (150)
Console.WriteLine("ch=" + e.HorizontalChange + " tw=" + categoriesToolBar.Width + " aw=" + categoriesToolBar.ActualWidth);
}
private void toolbarSizeChanged(object sender, SizeChangedEventArgs e) {
// this is not fired when I try to change the width in splitPaneDragging method
}
I have a DataGrid that LayoutTransform is Binded to a Slider like that:
<DataGrid.LayoutTransform>
<ScaleTransform
ScaleX="{Binding ElementName=MySlider, Path=Value}"
ScaleY="{Binding ElementName=MySlider, Path=Value}" />
</DataGrid.LayoutTransform>
</DataGrid>
<Slider x:Name="MySlider"
Minimum="0.3"
Maximum="2.0"
SmallChange="0.1"
LargeChange="0.1"
Value="1.0"
IsSnapToTickEnabled="True"
TickFrequency="0.1"
TickPlacement="TopLeft"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Width="200"
Margin="0,0,61,0" />
<TextBlock Name="Lstate"
Text="{Binding ElementName=MySlider, Path=Value, StringFormat={}{0:P0}}"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Width="50" Height="20"
Margin="0,0,0,1" />
Now, in the Code I have the PreviewMouseWheel event with the following Code:
bool handle = (Keyboard.Modifiers & ModifierKeys.Control) > 0;
if (!handle)
return;
double value;
if (e.Delta > 0)
value = 0.1;
else
value = -0.1;
MySlider.Value += value;
And my question is: How to scroll to the actual Mouse Position like AutoCad or some other programs?
Thanks
Sorry for my bad english...
I have a very very good solution now:
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
EnableColumnVirtualization="False"
EnableRowVirtualization="True"
ScrollViewer.CanContentScroll="True"
private void Data_OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
// Scroll to Zoom
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
// Prevent scroll
e.Handled = true;
var scrollview = FindVisualChild<ScrollViewer>(Data);
if (scrollview != null)
{
// The "+20" are there to account for the scrollbars... i think. Not perfectly accurate.
var relativeMiddle = new Point((Data.ActualWidth + 20) / 2 + (Mouse.GetPosition(Data).X - Data.ActualWidth / 2), (Data.ActualHeight + 20) / 2 + (Mouse.GetPosition(Data).Y - Data.ActualHeight / 2));
var oldLocation = Data.LayoutTransform.Transform(Data.PointFromScreen(relativeMiddle));
// Zoom
MySlider.Value += (e.Delta > 0) ? MySlider.TickFrequency : -MySlider.TickFrequency;
// Scroll
var newLocation = Data.LayoutTransform.Transform(Data.PointFromScreen(relativeMiddle));
// Calculate offset
var shift = newLocation - oldLocation;
if (scrollview.CanContentScroll)
{
// Scroll to the offset (Item)
scrollview.ScrollToVerticalOffset(scrollview.VerticalOffset + shift.Y / scrollview.ViewportHeight);
}
else
{
// Device independent Pixels
scrollview.ScrollToVerticalOffset(scrollview.VerticalOffset + shift.Y);
}
// Device independent Pixels
scrollview.ScrollToHorizontalOffset(scrollview.HorizontalOffset + shift.X);
}
}
}
It zooms to the Mouse Position on the Datagrid with and without virtualization.
I know this type of question has been asked before, But I have tried the suggestions to no avail, so hopefully some fresh eyes can help me on this.
I have a Custom Control, which is basically a Border with a grid inside which contains 2 textboxes and a button.
The control also has a octagon that is added in codebehind.
The button's click event does not fire. I gather it has probably got something to do with the controls above it getting the click event instead of the Button, but I don't know how to solve it. Or perhaps because the octagon is drawn afterwards in the code behind.
I have tried so many different things, moving the button, adding another grid, setting the background to Transparent. This is driving me nuts.
Below is the code.
XAML
<UserControl x:Class="HSCGym.UserControls.CustomErrorControl"
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" d:DesignHeight="800" d:DesignWidth="1000">
<Border x:Name="ErrorBorder" BorderBrush="Black" BorderThickness="2" CornerRadius="20" Background="White"
Width="900" Height="700"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Grid.Row="0" Grid.RowSpan="2">
<Border.Effect>
<DropShadowEffect BlurRadius="7" Direction="300" ShadowDepth="6" Color="Black" />
</Border.Effect>
<Grid x:Name="LayoutRoot" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent" >
<Grid.RowDefinitions>
<RowDefinition Height="55" />
<RowDefinition Height="55" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding ErrorText}" HorizontalAlignment="Center" FontSize="40" Grid.Row="0"/>
<TextBlock Text="{Binding ErrorText2}" HorizontalAlignment="Center" FontSize="40" Grid.Row="1"/>
<Button x:Name="btnExit" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="38,0,0,20"
Content="Exit" Height="50" Width="200" FontSize="20" Click="btnExit_Click"
Grid.Row="2"/>
</Grid>
</Border>
</UserControl>
And the Code Behind:
public partial class CustomErrorControl
{
public string ErrorText { get; set; }
public string ErrorText2 { get; set; }
public string StopText { get; set; }
private readonly int x;
private readonly int y;
private readonly int r;
public CustomErrorControl(int x, int y, int radius, string stopError)
{
InitializeComponent();
this.x = x;
this.y = y;
r = radius;
StopText = stopError;
DrawOctagon(this.x, this.y, r);
}
public void DrawOctagon(int x, int y, int R)
{
int r2 = (int)(R/Math.Sqrt(2));
// OuterOctagon
Point[] outerOctagon = new Point[8];
outerOctagon[0].X = x;
outerOctagon[0].Y = y - R;
outerOctagon[1].X = x + r2;
outerOctagon[1].Y = y - r2;
outerOctagon[2].X = x + R;
outerOctagon[2].Y = y;
outerOctagon[3].X = x + r2;
outerOctagon[3].Y = y + r2;
outerOctagon[4].X = x;
outerOctagon[4].Y = y + R;
outerOctagon[5].X = x - r2;
outerOctagon[5].Y = y + r2;
outerOctagon[6].X = x - R;
outerOctagon[6].Y = y;
outerOctagon[7].X = x - r2;
outerOctagon[7].Y = y - r2;
HexColor stop1Colour = new HexColor("#FFA30D0D");
HexColor stop2Colour = new HexColor("#FFCA0C0C");
HexColor stop3Colour = new HexColor("#FFF71212");
GradientStop gs1 = new GradientStop { Color = stop1Colour, Offset = 0.98 };
GradientStop gs2 = new GradientStop { Color = stop2Colour, Offset = 0.5 };
GradientStop gs3 = new GradientStop { Color = stop3Colour, Offset = 0.04 };
LinearGradientBrush gb = new LinearGradientBrush
{
StartPoint = new Point(0.77, 0.85),
EndPoint = new Point(0.13, 0.15),
GradientStops = new GradientStopCollection {gs1, gs2, gs3}
};
DropShadowEffect dse = new DropShadowEffect
{
BlurRadius = 8,
Color = Colors.Black,
Direction = 320,
ShadowDepth = 10.0,
Opacity = 0.5
};
Polygon octagonOuter = new Polygon
{
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
Fill = gb,
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 5,
StrokeDashArray = new DoubleCollection { 10, 0 },
Effect = dse,
Points = new PointCollection
{
new Point(outerOctagon[0].X, outerOctagon[0].Y),
new Point(outerOctagon[1].X, outerOctagon[1].Y),
new Point(outerOctagon[2].X, outerOctagon[2].Y),
new Point(outerOctagon[3].X, outerOctagon[3].Y),
new Point(outerOctagon[4].X, outerOctagon[4].Y),
new Point(outerOctagon[5].X, outerOctagon[5].Y),
new Point(outerOctagon[6].X, outerOctagon[6].Y),
new Point(outerOctagon[7].X, outerOctagon[7].Y),
}
};
double outerOctCenterY = octagonOuter.Points[6].Y - octagonOuter.Points[2].Y;
double outerOctCenterX = octagonOuter.Points[4].X - octagonOuter.Points[0].X;
var rotate = new RotateTransform { Angle = 22.8, CenterX = outerOctCenterX, CenterY = outerOctCenterY };
octagonOuter.RenderTransform = rotate;
Grid.SetRow(octagonOuter, 2);
TextBlock tbStopError = new TextBlock
{
Text = StopText,
Foreground = new SolidColorBrush(Colors.White),
FontFamily = new FontFamily("Courier New"),
FontSize = 80,
FontWeight = FontWeights.Bold,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
};
Grid.SetRow(tbStopError, 2);
LayoutRoot.Children.Add(octagonOuter);
LayoutRoot.Children.Add(tbStopError);
}
private void btnExit_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Clicked");
}
}
Any ideas?
Many thanks
Neill
Update:
I have tested quickly putting MouseLeftButtonUp events on the grid and the border.
When I click the border, first the grid event fires, then the border event fires, but if I click the button, nothing fires. Does this mean the Button is above the grid and border so should be firing the event?
I will try some of the other suggestions as well in the meant time.
Neill
Update 2:
It gets stranger. I moved the button to the top and then click event fires. What I see is that from a certain Y point and below, absolutely no mouseleftbuttonup or click events fire, nada. This makes no sense. If I go from the bottom and click moving slowly up, then at a certain point, all events start firing.
Neill
Update 3
Hi all,
After removing controls one by one to see what is causing the issue, I now know it is the Border control. If I remove it, all works fine, when it is back, same problem as before. Any ideas?
Thanks
Neill
Update 4
Hi all,
OK so I finally sorted out the problem, so in case anybody finds themselves in a similar situation. It had nothing to do with that page at all. I am loading the page in a frame from a different page and what it seems is that I had to many row definitions set in the grid on the main page. After I corrected that, everything works fine.
Thanks
First try fill button by Transparent color (00000000).
Also you can send solution or part of it to me and I shall try to help.