as the title says I'm looking for a way to get the Window object the sender is in.
In my situation I have a window in which there is only one button:
<Window x:Class="WpfApp1.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"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200">
<Grid>
<Button Click="Button_Click"/>
</Grid>
</Window>
By clicking the button, it generates a new window without borders and a red rectangle inside it:
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window window = new Window();
window.Height = window.Width = 100;
window.WindowStyle = WindowStyle.None;
Rectangle rectangle = new Rectangle();
rectangle.Height = rectangle.Width = 50;
rectangle.Fill = new SolidColorBrush(Colors.Red);
Grid grid = new Grid();
grid.Children.Add(rectangle);
window.Content = grid;
window.Show();
}
}
}
My idea was to capture the MouseDown event on the rectangle to move the window (using DragMove()), unfortunately I have no idea how to get the Window object on which the rectangle that invoked the event is located, any idea to do it?.
You can use MouseDown event after new Rectangle and find Window through Parent property like this:
private void Rectangle_MouseDown(object sender, MouseButtonEventArgs e)
{
var rect = sender as Rectangle;
FrameworkElement framework = rect;
while (framework != null && (framework as Window)==null && framework.Parent!= null)
{
framework = framework.Parent as FrameworkElement;
}
if(framework is Window window)
{
//here you has found the window
}
}
Related
I have a WPF application for drawing (Like Microsoft Paint) and I use WriteableBitmap and subscribe mouse event to do that.
It works perfectly and the ink tracks my mouse immediately when I run the program by Visual Studio no matter in debug mode or release mode
However, I get bad performance when I drawing by running the program exe file (\bin\Release\net5.0-windows\oooo.exe OR \bin\Debug\net5.0-windows\oooo.exe). the ink is drawn slower and has some delay after the mouse moving when drawing.
The difference (click to view gifs):
Running the program by Visual Studio
Running the program exe file
Even I build this application on local, I got the same bad result. Is there any thing I can fix this?
The code is as follows:
MainWindow.xaml
<Window x:Class="paint_test.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" Height="900" Width="1600"
Loaded="Window_Loaded">
<Grid>
<Canvas Name="MainCanvas" Background="Bisque"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace paint_test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MainCanvas.Children.Add(new PaintCanvas((int)MainCanvas.ActualWidth, (int)MainCanvas.ActualHeight));
}
}
}
PaintCanvas.cs
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace paint_test
{
public class PaintCanvas : System.Windows.Controls.Image
{
WriteableBitmap fullWriteableBmp;
int paintingSize = 10;
Color paintingColor = Colors.Blue;
public PaintCanvas(int width, int height)
{
RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.NearestNeighbor);
RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);
fullWriteableBmp = BitmapFactory.New(width, height);
Source = fullWriteableBmp;
MouseDown += PaintCanvas_MouseDown;
MouseMove += PaintCanvas_MouseMove;
}
private void PaintCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed) return;
DrawPixel(e);
}
private void PaintCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed) return;
DrawPixel(e);
}
private void DrawPixel(MouseEventArgs e)
{
int x1 = (int)e.GetPosition(this).X - (paintingSize / 2);
int y1 = (int)e.GetPosition(this).Y - (paintingSize / 2);
int x2 = (int)e.GetPosition(this).X + (paintingSize / 2);
int y2 = (int)e.GetPosition(this).Y + (paintingSize / 2);
fullWriteableBmp.FillEllipse(x1, y1, x2, y2, paintingColor);
}
}
}
I have the following program that creates Rectangle using Rect object. I want to attach Mouse events to newly created Rect. How can I do that? Please help. Here's the code below.
XAML
<Window x:Class="TestDrawing.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"
xmlns:local="clr-namespace:TestDrawing"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Canvas>
<Button x:Name="BtnAddRectangle" Content="Add Rectngle" Click="BtnAddRectangle_Click" Height="20"/>
</Canvas>
</StackPanel>
<Canvas Name="canvas">
</Canvas>
</Grid>
CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TestDrawing
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
bool drag = false;
Point startPoint;
public MainWindow()
{
InitializeComponent();
}
// this creates and adds rectangles dynamically
private void BtnAddRectangle_Click(object sender, RoutedEventArgs e)
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
Rect rect = new Rect(new Size(150, 100));
drawingContext.DrawRectangle(Brushes.LightBlue, null,rect);
drawingContext.Close();
canvas.Children.Add(new VisualHost { Visual = drawingVisual });
foreach (UIElement child in canvas.Children)
{
//Not working
child.MouseDown += rectangle_MouseDown;
child.MouseMove += rectangle_MouseMove;
}
}
private void rectangle_MouseDown(object sender, MouseButtonEventArgs e)
{
// start dragging
drag = true;
// save start point of dragging
startPoint = Mouse.GetPosition(canvas);
}
private void rectangle_MouseMove(object sender, MouseEventArgs e)
{
// if dragging, then adjust rectangle position based on mouse movement
if (drag)
{
Rectangle draggedRectangle = sender as Rectangle;
Point newPoint = Mouse.GetPosition(canvas);
double left = Canvas.GetLeft(draggedRectangle);
double top = Canvas.GetTop(draggedRectangle);
Canvas.SetLeft(draggedRectangle, left + (newPoint.X - startPoint.X));
Canvas.SetTop(draggedRectangle, top + (newPoint.Y - startPoint.Y));
startPoint = newPoint;
}
}
private void rectangle_MouseUp(object sender, MouseButtonEventArgs e)
{
// stop dragging
drag = false;
}
}
public class VisualHost : UIElement
{
public Visual Visual { get; set; }
protected override int VisualChildrenCount
{
get { return Visual != null ? 1 : 0; }
}
protected override Visual GetVisualChild(int index)
{
return Visual;
}
}
}
Here's a quote from the MSDN page on Using DrawingVisual Objects:
The DrawingVisual is a lightweight drawing class that is used to render shapes, images, or text. This class is considered lightweight because it does not provide layout or event handling, which improves its performance. For this reason, drawings are ideal for backgrounds and clip art.
If you need event handling, why not just use the existing Rectangle class? Your existing code can be easily changed to use this class instead of your custom VisualHost.
private void BtnAddRectangle_Click(object sender, RoutedEventArgs e)
{
Rectangle rect = new Rectangle() { Fill = Brushes.LightBlue, Width = 150, Height = 100 };
canvas.Children.Add(rect);
foreach (UIElement child in canvas.Children)
{
child.MouseDown += rectangle_MouseDown;
child.MouseMove += rectangle_MouseMove;
}
}
like some other people here i have a ListView (updated via binding in a GridView).
I want to keep the last inserted Item in the View. So i tried
LView.ScrollIntoView(LView.Items[LView.Items.Count - 1]);
This is working almost fine. Altough the first item which would have to be scrolled into view is only shown like 80% of its whole row (depending on how high i define the whole ListView, i almost got 100%).
The real problem is that the following items which should get scrolled into view are not shown. It is also noticable at the Scrollbar itself which is not at the bottom.
Last Item is not shown
Here is the code of my MainWindow.
public partial class MainWindow : Window
{
private InterfaceCtrl ICtrl;
private ListView LView;
public MainWindow()
{
InitializeComponent();
this.ICtrl = new InterfaceCtrl();
this.ICtrl.ProgressCollection.CollectionChanged += this.CollectionChanged;
Grid MainGrid = new Grid();
this.Content = MainGrid;
GridView gv = new GridView();
Binding StartTimeStampBinding = new Binding() { Path = new PropertyPath("StartTS"), Mode = BindingMode.OneWay, StringFormat = "dd.MM.yyyy - HH:mm:ss.fff" };
GridViewColumn gvTCStartTS = new GridViewColumn() { Header = "Time", Width = 150.00, DisplayMemberBinding = StartTimeStampBinding };
gv.Columns.Add(gvTCStartTS);
LView = new ListView() { Height = 192, Width = 250, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, View = gv, ItemsSource = this.ICtrl.ProgressCollection };
MainGrid.Children.Add(LView);
ICtrl.StartMyThread();
}
private void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new System.Action(delegate ()
{
if (LView != null && LView.Items.Count > 0)
{
LView.UpdateLayout();
//LView.ScrollIntoView(LView.Items[LView.Items.Count - 1]);
LView.SelectedIndex = LView.Items.Count;
LView.ScrollIntoView(LView.SelectedItem);
}
}));
}
}
Thank you.
EDIT:
It seemed to be a timing problem, although all the wanted data was in the LView at the right time i tried a workaround with a Textbox bound to the Timestamp.
TextBox tb = new TextBox(); // { Width = 250, Height = 28, Margin= new Thickness(10,100,1,0)};
tb.SetBinding( TextBox.TextProperty , new Binding("LastMsgTimestamp") { Source = this.ICtrl, Mode = BindingMode.OneWay, StringFormat = "dd.MM.yyyy - HH:mm:ss.fff" });
tb.TextChanged += this.UpdateScrollbar;
tb.Visibility = Visibility.Hidden;
It seems to me like there is a timing issue within the binding to the LView and the fired Event of the ObservableCollection. This also includes the PropertyChanged of the ObservableCollection.
I tried the events TargetUpdated and SoruceUpdated directly within LView but those didn't came up at all.
You could try to call any of the ScrollToBottom() or ScrollToVerticalOffset() methods of the ListView's internal ScrollViewer element:
private void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new System.Action(delegate ()
{
if (LView != null && LView.Items.Count > 0)
{
LView.UpdateLayout();
ScrollViewer sv = GetChildOfType<ScrollViewer>(LView);
if (sv != null)
sv.ScrollToBottom();
LView.SelectedIndex = LView.Items.Count;
LView.ScrollIntoView(LView.SelectedItem);
}
}));
}
private static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null)
return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null)
return result;
}
return null;
}
I have made the following sample. You could try to call ScrollToBottom in inner ScrollViewer as #mm8 points out. Nevertheless when saw the answer I was already making my sample, so here it is:
Codebehind
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ListViewScroll
{
public partial class MainWindow : Window
{
public ObservableCollection<string> Names { get; set; }
public MainWindow()
{
InitializeComponent();
Names = new ObservableCollection<string>();
ListView.ItemsSource = Names;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Names.Add("Some Name" + ++someInt);
// Get the border of the listview (first child of a listview)
var border = VisualTreeHelper.GetChild(ListView, 0) as Decorator;
// Get scrollviewer
var scrollViewer = border.Child as ScrollViewer;
scrollViewer.ScrollToBottom();
}
private static int someInt;
}
}
XAML
<Window x:Class="ListViewScroll.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">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView Grid.Row="0" Name="ListView"/>
<Button Content="Add" FontSize="20" Grid.Row="1"
Click="ButtonBase_OnClick"/>
</Grid>
</Window>
In this case I am handling the scrolling in the button click event but you may change this to fit your requirements
It works, I have tested.
Hope this helps
How can I position a button img randomly on a grid in XAML? I tried it, but it doesn't work!
This is my code:
public void randomButton()
{
Button newBtn = new Button();
newBtn.Content = "A New Button";
panelButton.Children.Add(newBtn);
Grid.SetRow(newBtn, 1);
Random generator = new Random();
newBtn = generator.Next(1, 100);
}
You need to set the Grid.Row dependency property on the Button.
XAML
<Window x:Class="WpfApplication1.MainWindow" [...] Loaded="Window_Loaded">
<Grid Name="grdMain">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
</Grid>
</Window>
C#
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
void Window_Loaded(object sender, RoutedEventArgs e)
{
//creating the button
Button b = new Button() { Content = "Click me!" };
//when clicked, it'll move to another row
b.Click += (s, ea) => ChangeButtonRow(s as Button);
//adding the button to the grid
grdMain.Children.Add(b);
//calling the row changing method for the 1st time, so the button will appear in a random row
ChangeButtonRow(b);
}
void ChangeButtonRow(Button b)
{
//setting the Grid.Row dep. prop. to a number that's a valid row index
b.SetValue(Grid.RowProperty, new Random().Next(0, grdMain.RowDefinitions.Count));
}
}
}
I hope this helps. :)
i am building an application to show all the software installed in the computer, i already have all the buttons to show with the respective icon, but when i show them, the uniformgrid only shows the buttons that fit in to the window, i thought a scrollbar will show them, but i get to the end of the window and the buttons still missing! how can i show them all with a scrollbar?
Here is the XAML code:
<Window x:Class="apple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Grid>
<DockPanel Name="dock">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<UniformGrid Name="gridx" DockPanel.Dock="Top" Rows="7" Columns="7">
</UniformGrid>
</ScrollViewer>
</DockPanel>
</Grid>
</Window>
Here is the c# code:
namespace apple
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public string[] link = Directory.GetFiles(#"C:\ProgramData\Microsoft\Windows\Start Menu\Programs", "*.lnk", SearchOption.AllDirectories);
public MainWindow()
{
this.ResizeMode = ResizeMode.NoResize;
//this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
InitializeComponent();
masterGUI();
}
public void masterGUI()
{
gridx.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
IconImage[] ico = null;
Bitmap[] img = null;
string[] list = null;
list = new string[link.Length];
ico = new Icon[link.Length];
img = new Bitmap[link.Length];
for (int n = 0; n < link.Length; n++)
{
ImageBrush ib = new ImageBrush();
System.Windows.Controls.Button newBtn = new Button();
list[n] = System.IO.Path.GetFileNameWithoutExtension(link[n]);
FileToImageIconConverter some = new FileToImageIconConverter(link[n]);
ImageSource imgSource = some.Icon;
ib.ImageSource = imgSource;
newBtn.Name = "a" + n;
newBtn.Background = ib;
newBtn.Content = list[n];
newBtn.Click += new RoutedEventHandler(newBtn_Click);
gridx.Children.Add(newBtn);
}
}
private void newBtn_Click(object sender, RoutedEventArgs e)
{
Button clicked = (Button)sender;
string test = null;
test = clicked.Name.Replace("a","0");
this.Close();
System.Diagnostics.Process.Start(link[Int32.Parse(test)]);
}
}
}
Remove the Grid and DockPanel and set either UniformGrid.Rows or UniformGrid.Columns, not both. All you need is Window, ScrollViewer, and UniformGrid:
<Window>
<ScrollViewer>
<UniformGrid Name="gridx" Columns="7"/>
</ScrollViewer>
</Window>
And to do it in a more idiomatic WPF fashion, you should have something like this:
<Window>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Programs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="7"/>
You would then expose a Programs collection from your data source and would thus automatically generate an item for each installed program.