public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Button button1 = new Button();
Point tPosition = Mouse.GetPosition(this);
button1.Margin = new Thickness(tPosition.X,tPosition.Y,0,0) ;
button1.Width = 75;
this.AddChild(button1);
}
}
I think the code is pretty self-explanatory, I used a code that is approved in other thread here in stackoverflow, it doesn't give any error, but it also doesn't show up, do I need to refresh the window? And how?
by request, the XAML
<Window x:Class="Ampeldingensthingy.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 MouseLeftButtonDown="Grid_MouseLeftButtonDown" Name="hans">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="398*" />
<ColumnDefinition Width="105*" />
</Grid.ColumnDefinitions>
</Grid>
</Window>
Window can have only one children and I think you already have one that is Grid whose MouseButtonDown you subscribing. Give a name to that Grid and then replace
this.AddChild(button1);
with
grid.Children.Add(button1);
here grid is the name given to Grid.
The object "this" depicts the Window object here. Content of a ContentControl must be a single element. Hence append the Button that is "button1" inside a parent Grid. For example
private void Button_Click(object sender, RoutedEventArgs e)
{
Button button1 = new Button();
Point tPosition = Mouse.GetPosition(this);
button1.Margin = new Thickness(tPosition.X, tPosition.Y, 0, 0);
button1.Width = 75;
MainGrid.Children.Add(button1);
}
And in your Xaml
<Grid x:Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Click="Button_Click" Content="click" Grid.Row="1"/>
</Grid>
public partial class MainWindow : Window
{
public StackPanel SPanel{get;set;}
public MainWindow()
{
InitializeComponent();
SPanel = new StackPanel { Orientation = Orientation.Vertical };
}
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Button button1 = new Button();
Point tPosition = Mouse.GetPosition(this);
button1.Margin = new Thickness(tPosition.X,tPosition.Y,0,0) ;
button1.Width = 75;
SPanel.Children.Add(button1);
window.Content = SPanel;
}
}
try to do the same with a canvas. the buttons will popup at the position you gave them.
Related
Due to project privacy protection, I can't paste the product code here. So I made a simple example to show the problem.
I have a window like this:
When I click the Right button, I want the Right Column hide and the Left Column Stretch to full window:
Here is my Xaml:
<Window x:Class="WpfApplication1.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.ColumnDefinitions>
<ColumnDefinition MinWidth="0"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition x:Name="rightCol" MinWidth="0"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button Height="100" Content="Left" />
</StackPanel>
<GridSplitter Grid.Column="1" Width="10" VerticalAlignment="Stretch" HorizontalAlignment="Center"/>
<StackPanel x:Name="right" Grid.Column="2" >
<Button Height="100" Content="Right" Click="Button_Click" />
</StackPanel>
</Grid>
</Window>
And here is my code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var b = new Binding("Visibility")
{
Source = this.right
};
var style = new Style(typeof(ColumnDefinition))
{
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(300)),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
};
style.Triggers.Add(new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed,
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(0)),
new Setter(ColumnDefinition.MaxWidthProperty, double.PositiveInfinity),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
});
this.rightCol.Style = style;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.right.Visibility = System.Windows.Visibility.Collapsed;
}
}
When I run it and Click the Right button, it works fine. But if I drag the GridSplitter before I Click the Right button, the trigger doesn't work:
Why this happened?
Note:
If I use below code, it can work.
private void Button_Click(object sender, RoutedEventArgs e)
{
this.right.Visibility = System.Windows.Visibility.Collapsed;
this.rightCol.Width = new GridLength(0);
}
But is there any way to use trigger?
I got my answer from this link:
GridSplitter overrides ColumnDefinition's style trigger?
So below code works fine:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var b = new Binding("Visibility")
{
Source = this.rightPane,
Mode = BindingMode.OneWay
};
var style = new Style(typeof(ColumnDefinition))
{
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(300)),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
};
/*
style.Triggers.Add(new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed,
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(0)),
new Setter(ColumnDefinition.MaxWidthProperty, double.PositiveInfinity),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
});*/
Storyboard sb = new Storyboard();
ObjectAnimationUsingKeyFrames oaf = new ObjectAnimationUsingKeyFrames();
DiscreteObjectKeyFrame disobj = new DiscreteObjectKeyFrame();
disobj.KeyTime = TimeSpan.FromMilliseconds(0);
disobj.Value = GridLength.Auto;
oaf.KeyFrames.Add(disobj);
sb.Children.Add(oaf);
Storyboard.SetTargetProperty(oaf, new PropertyPath(ColumnDefinition.WidthProperty));
var actionBegin = new BeginStoryboard();
actionBegin.Storyboard = sb;
actionBegin.Name="BeginStoryboard1";
var actionEnd = new RemoveStoryboard();
actionEnd.BeginStoryboardName = "BeginStoryboard1";
var trigger = new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed
};
trigger.EnterActions.Add(actionBegin);
trigger.ExitActions.Add(actionEnd);
style.Triggers.Add(trigger);
style.RegisterName(actionBegin.Name, actionBegin);
this.rightCol.Style = style;
}
When I create ListBox with virtualization enabled and then update all its items appearance it works very fast. But when i slowly scroll down all items in ListBox and then update all items appearance it takes a lot of time. I think it because VirtualizingStackPanel does not destroy items when they are runs out of viewport.
I wrote simple app to reproduce this behavior.
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for(int i = 0; i < 5000; ++i) // creating 5k text boxes
MyList.Items.Add(new TextBox() { Text = CurrText });
}
private void Button_Click(object sender, RoutedEventArgs e)
{
GC.Collect();
n = (n + 1) % 2; // switch 0 to 1 or 1 to 0
foreach (var item in MyList.Items)
((TextBox)item).Text = CurrText; // set new text
}
static int n = 0;
string CurrText { get { return new string(n.ToString()[0], 50); } }
}
XAML:
<Window x:Class="VPanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="700" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Name="MyList" VirtualizingStackPanel.IsVirtualizing="True"/>
<Button Grid.Row="1" Content="UpdateText" Click="Button_Click"/>
</Grid>
</Window>
Clicking a button "UpdateText" updates all textboxes text. If slowly scroll to end by dragging scroller, "UpdateText" button clicks with a huge lag.
Don't create TextBoxes manually.
The word "virtualize" refers to a technique by which a subset of user interface (UI) elements are generated from a larger number of data items based on which items are visible on-screen. Generating many UI elements when only a few elements might be on the screen can adversely affect the performance of your application.
You create your UI items manually so it's already too late for virtualization. Use bindings and it will create TextBox from ItemTemplate whenever it is required. It will also not refresh TextBox.Text value if it's not currently in the view. To do that change your MainWindow to create ObservableCollection instead of TextBoxes and operate on that:
public partial class MainWindow : Window
{
private readonly ObservableCollection<string> _textBoxes = new ObservableCollection<string>();
public ICollection<string> TextBoxes { get { return _textBoxes; } }
private int n = 0;
private string CurrText { get { return new string(n.ToString()[0], 50); } }
public MainWindow()
{
for (int i = 0; i < 5000; ++i) _textBoxes.Add(CurrText);
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
n = (n + 1) % 2; // switch 0 to 1 or 1 to 0
for (int i = 0; i < _textBoxes.Count; i++) _textBoxes[i] = CurrText;
}
}
and then change XAML to bind to TextBoxes list property
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding TextBoxes}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=., Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="UpdateText" Click="Button_Click"/>
</Grid>
I have a project that uses a System.Windows.Controls.Primatives.Popup to drag a 'tooltip' like control along with a mouse.
Whenever the drag crosses a horizontal line the popup 'wraps' to the bottom of the screen - despite having sane values for the VerticalOffset. The point at which this wrapping occurs appears to be tied to the HEIGHT of the window, but not it's position.
Here's the code from the sandbox project I have created that also exhibits the same behavior:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MainGrid.MouseDown += Grid_MouseDown;
this.MainGrid.MouseUp += Grid_MouseUp;
this.MainGrid.MouseMove += (s, e) => { if (this.Popup.IsOpen) { Popup_Drag(s, e); } };
this.Popup.MouseMove += Popup_Drag;
}
private void Popup_Drag(object sender, MouseEventArgs e)
{
Popup.HorizontalOffset = e.GetPosition(this.Popup).X;
Popup.VerticalOffset = e.GetPosition(this.Popup).Y;
this.Status_Top.Text = String.Format("Height/Top: {0}/{1} Width/Left: {2}/{3}", this.Height, this.Top, this.Width, this.Left);
this.Status.Text = String.Format("Vertical Offset: {0} Horizontal Offset: {1}", Popup.VerticalOffset, Popup.HorizontalOffset);
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
this.Popup.IsOpen = false;
}
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
this.Popup.IsOpen = true;
Popup_Drag(sender, e);
}
}
And the Window XAML:
<Window x:Class="WpfSandbox.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 x:Name="MainGrid" Background="Purple">
<TextBlock x:Name="Status_Top"></TextBlock>
<Popup x:Name="Popup" Cursor="Hand" HorizontalAlignment="Left"
VerticalAlignment="Bottom" IsOpen="True">
<TextBlock Background="Blue" Foreground="White">
<TextBlock x:Name="Status">TEXT</TextBlock></TextBlock>
</Popup>
</Grid>
</Window>
I was able to fix this by adding Placement="RelativePoint" to the Popup attributes. Apparently this is the default in Silverlight, but not WPF.
I have a dataform with a richtextbox In it. The user can type some text and have some editing capability, but I'd like to give the user the option to expand the editor to fullscreen to have more richtextbox editing options. How can I implement a function that will allow me to fullscreen (or atleast create a bigger window) the richtexteditor so the user has better overview over the document and more editing options?
This is ment to be possible in OOB mode.
Full screen wont work as you have limit keyboard input in fullscreen:
Up Arrow
Down Arrow
Left Arrow
Right Arrow
Spacebar
Tab
Page Up
Page Down
Home
End
Enter
What you can do is for example is make your element fill the whole space of your silverlight application by making it the exact size of your RootVisual and adjusting your margins to place it correctly in your application:
XAML:
<UserControl x:Class="SilverlightApplication1.MyRichTextBox"
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="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button x:Name="FullScreen" Grid.Row="0" Content="FullScreen" Click="FullScreen_Click" />
<RichTextBox Grid.Row="1" />
</Grid>
Code-behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace SilverlightApplication1
{
public partial class MyRichTextBox : UserControl
{
private Thickness _oldMargin;
private double _oldHeight = double.NaN;
private double _oldWidth = double.NaN;
private HorizontalAlignment _oldHorizontalAlignment;
private VerticalAlignment _oldVerticalAlignment;
private bool _fullScreen = false;
public MyRichTextBox()
{
InitializeComponent();
}
private void FullScreen_Click(object sender, RoutedEventArgs e)
{
if (_fullScreen)
{
_fullScreen = false;
Margin = _oldMargin;
Width = _oldWidth;
Height = _oldHeight;
}
else
{
_fullScreen = true;
_oldMargin = Margin;
_oldWidth = Width;
_oldHeight = Height;
_oldHorizontalAlignment = HorizontalAlignment;
_oldVerticalAlignment = VerticalAlignment;
FrameworkElement rootVisual = Application.Current.RootVisual as FrameworkElement;
GeneralTransform generalTransform = TransformToVisual(rootVisual);
Point position = generalTransform.Transform(new Point(0, 0));
Width = rootVisual.ActualWidth;
Height =rootVisual.ActualHeight;
Margin = new Thickness(-position.X - 1, -position.Y - 1
, (ActualWidth + position.X) - rootVisual.ActualWidth - 1
, (ActualHeight + position.Y) - rootVisual.ActualHeight - 1);
}
}
}
}
I've got a StackPanel, and it's perfect for laying out columns that the user adds at runtime. But I'd like the columns to be resizable, and I was reading about the GridSplitter control. Here's what I'm wondering:
Is the GridSplitter the wpf replacement for the WinForms splitter? In other words, is this the de facto way to allow the users to resize regions of the window?
Does it only work inside of a Grid? If I have items inside a stackpanel or a dockpanel, can I still use a gridsplitter the way I used the splitter in WinForms?
If I have to use a Grid, how can I make it behave just like a StackPanel? (hope it doesn't come to that)
GridSplitter only works in a Grid and is the easiest way to allow users to resize controls.
What do you mean that you want your grid (with gridsplitters) to behave just like a stackpanel? A stackpanel will exactly fit each of its children while a grid with gridsplitters will leave it up to the user.
Below is a user control which allows items to be added as columns. Between columns are grid splitters. Users can click on Delete button to remove added columns and columns can be added using behind code. Let me know if that's what you were looking for.
User control SmartGrid XAML:
<UserControl x:Class="SmartGridDemo.SmartGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Name="_grid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
</Grid>
</UserControl>
User control SmartGrid code behind:
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace SmartGridDemo
{
public partial class SmartGrid : UserControl
{
public SmartGrid()
{
InitializeComponent();
}
public void Add(UIElement child)
{
int columnIndex = _grid.ColumnDefinitions.Count();
_grid.ColumnDefinitions.Add(
new ColumnDefinition()
{
Width = new GridLength(columnIndex == 0 ? 0 :5)
});
GridSplitter gridSplitter =
new GridSplitter()
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
ResizeDirection = GridResizeDirection.Columns,
Background = Brushes.Black
};
_grid.Children.Add(gridSplitter);
Grid.SetColumn(gridSplitter, columnIndex);
Grid.SetRow(gridSplitter, 0);
Grid.SetRowSpan(gridSplitter, 2);
columnIndex++;
_grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
Button button = new Button();
button.Content = "Delete";
button.Tag = new TagTuple() {Child = child, GridSplitter = gridSplitter};
button.Click += new RoutedEventHandler(DeleteButton_Click);
_grid.Children.Add(button);
Grid.SetColumn(button, columnIndex);
Grid.SetRow(button, 0);
_grid.Children.Add(child);
Grid.SetColumn(child, columnIndex);
Grid.SetRow(child, 1);
}
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
int columnIndex = Grid.GetColumn(button);
TagTuple tagTuple = button.Tag as TagTuple;
_grid.Children.Remove(tagTuple.GridSplitter);
_grid.Children.Remove(tagTuple.Child);
_grid.Children.Remove(button as UIElement);
_grid.ColumnDefinitions.RemoveAt(_grid.ColumnDefinitions.Count() - 1);
_grid.ColumnDefinitions.RemoveAt(_grid.ColumnDefinitions.Count() - 1);
foreach (UIElement child in _grid.Children)
{
int columnIndexForChild = Grid.GetColumn(child);
if (columnIndexForChild > columnIndex)
{
Grid.SetColumn(child, columnIndexForChild - 2);
}
}
}
private class TagTuple
{
public GridSplitter GridSplitter { get; set; }
public UIElement Child { get; set; }
}
}
}
Demo code, add some text in the TextBox and hit Add button to add new columns, XAML:
<Window x:Class="SmartGridDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SmartGridDemo"
Title="SmartGridDemo" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Name="_texBox" Grid.Row="0" Grid.Column="0" />
<Button Content="Add" Click="AddButton_Click" Grid.Row="0" Grid.Column="1" />
<local:SmartGrid x:Name="_smartGrid" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" />
</Grid>
</Window>
Demo, behind code:
using System;
using System.Windows;
using System.Windows.Controls;
namespace SmartGridDemo
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
_smartGrid.Add(new TextBlock() { Text = "AAA" });
_smartGrid.Add(new TextBlock() { Text = "BBB" });
_smartGrid.Add(new TextBlock() { Text = "CCC" });
_smartGrid.Add(new TextBlock() { Text = "DDD" });
_smartGrid.Add(new TextBlock() { Text = "EEE" });
_smartGrid.Add(new TextBlock() { Text = "FFF" });
}
private void AddButton_Click(object sender, RoutedEventArgs e)
{
_smartGrid.Add(new TextBlock() { Text = _texBox.Text });
}
}
}
Here is an open source WPF SplitContainer:
[http://wpfsplitcontainer.codeplex.com/]