Panel in a panel, autoscroll - c#

I'm doing a minimalist test app after encountering an issue with my real program, using WinForms. I put a small panel (child) inside a bigger panel (parent). The bigger panel has AutoScroll set to true. The child panel has the default Anchors set to Top and Left. The child panel is not docked.
The behavior I want is for scrollbars to appear whenever the smaller panel's location is too offset, either top, bottom, left or right. The problem is that it only works when it's too far right, or too far in the bottom. No scrollbars appear when it's too much in the top or too much in the left directions.
I use two simple buttons to force the child panel's location 200 pixels to the left, or 200 pixels to the right to have a quick way of easily modifying its position.
Here's my Form1() code:
private void button1_Click(object sender, EventArgs e)
{
childPanel.Location = new Point(childPanel.Location.X - 200, childPanel.Location.Y);
hostPanel.Invalidate();
}
private void button2_Click(object sender, EventArgs e)
{
childPanel.Location = new Point(childPanel.Location.X + 200, childPanel.Location.Y);
hostPanel.Invalidate();
}
Here's the designer code:
private void InitializeComponent()
{
this.hostPanel = new System.Windows.Forms.Panel();
this.childPanel = new System.Windows.Forms.Panel();
this.moveChildLeft = new System.Windows.Forms.Button();
this.moveChildRight = new System.Windows.Forms.Button();
this.hostPanel.SuspendLayout();
this.SuspendLayout();
//
// hostPanel
//
this.hostPanel.AutoScroll = true;
this.hostPanel.BackColor = System.Drawing.SystemColors.AppWorkspace;
this.hostPanel.Controls.Add(this.childPanel);
this.hostPanel.Location = new System.Drawing.Point(239, 48);
this.hostPanel.Name = "hostPanel";
this.hostPanel.Size = new System.Drawing.Size(400, 400);
this.hostPanel.TabIndex = 0;
//
// childPanel
//
this.childPanel.BackColor = System.Drawing.SystemColors.ButtonHighlight;
this.childPanel.Location = new System.Drawing.Point(29, 62);
this.childPanel.Name = "childPanel";
this.childPanel.Size = new System.Drawing.Size(342, 259);
this.childPanel.TabIndex = 0;
//
// moveChildLeft
//
this.moveChildLeft.Location = new System.Drawing.Point(61, 81);
this.moveChildLeft.Name = "moveChildLeft";
this.moveChildLeft.Size = new System.Drawing.Size(75, 23);
this.moveChildLeft.TabIndex = 1;
this.moveChildLeft.Text = "Left 200";
this.moveChildLeft.UseVisualStyleBackColor = true;
this.moveChildLeft.Click += new System.EventHandler(this.button1_Click);
//
// moveChildRight
//
this.moveChildRight.Location = new System.Drawing.Point(61, 111);
this.moveChildRight.Name = "moveChildRight";
this.moveChildRight.Size = new System.Drawing.Size(75, 23);
this.moveChildRight.TabIndex = 2;
this.moveChildRight.Text = "Right 200";
this.moveChildRight.UseVisualStyleBackColor = true;
this.moveChildRight.Click += new System.EventHandler(this.button2_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1018, 549);
this.Controls.Add(this.moveChildRight);
this.Controls.Add(this.moveChildLeft);
this.Controls.Add(this.hostPanel);
this.Name = "Form1";
this.Text = "Form1";
this.hostPanel.ResumeLayout(false);
this.ResumeLayout(false);
}

Yet - Another Winforms incapability quickly solved by WPF:
XAML:
<Window x:Class="WpfApplication4.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window3" WindowState="Maximized">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="Left" Click="MoveLeft"/>
<Button Content="Right" Click="MoveRight"/>
</StackPanel>
<Border BorderBrush="Blue" BorderThickness="1" Width="300" Height="300">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" x:Name="Scr">
<Grid Background="Green" Width="100" Height="100" x:Name="Grid"/>
</ScrollViewer>
</Border>
</DockPanel>
</Window>
Code behind:
using System.Windows;
namespace WpfApplication4
{
public partial class Window3 : Window
{
public Window3()
{
InitializeComponent();
}
private void MoveRight(object sender, RoutedEventArgs e)
{
if (Grid.Margin.Right <= 0)
{
Grid.Margin = new Thickness(Grid.Margin.Left + 100,0,0,0);
}
else
{
Grid.Margin = new Thickness(0, 0, Grid.Margin.Right - 100, 0);
Scr.ScrollToHorizontalOffset(Scr.HorizontalOffset - 100);
}
}
private void MoveLeft(object sender, RoutedEventArgs e)
{
if (Grid.Margin.Left > 0)
{
Grid.Margin = new Thickness(Grid.Margin.Left - 100, 0, 0, 0);
}
else
{
Grid.Margin = new Thickness(0, 0, Grid.Margin.Right + 100, 0);
Scr.ScrollToHorizontalOffset(Scr.HorizontalOffset + 100);
}
}
}
}
Copy and paste my code in a File -> New -> WPF Application and see the results for yourself.

Eventually, you might want to convert your app to WPF. Since, Winform is condemned to a small death.

Related

TabControl overlays Minimize, Maximize and Close button of window

If I create for example 12 Tabs at start of my app the tabpages are overlapping the Close, Minimize and Maximize buttons,
I have used the official sample from Microsoft:
https://github.com/microsoft/WinUI-Gallery/blob/master/XamlControlsGallery/TabViewPages/TabViewWindowingSamplePage.xaml.cs
I only changed the number of tabs created at start to 12 like this:
// Main Window -- add some default items
for (int i = 0; i < 12; i++)
{
Tabs.TabItems.Add(CreateNewTVI($"Item {i}", $"Page {i}"));
}
And then this happens:
And I really don't know why this happens
Edit:
The Xaml Code:
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<muxc:TabView x:Name="Tabs"
VerticalAlignment="Stretch"
<muxc:TabView.TabStripHeader>
<Grid x:Name="ShellTitlebarInset" Background="Transparent" />
</muxc:TabView.TabStripHeader>
<muxc:TabView.TabStripFooter>
<Grid x:Name="CustomDragRegion" Background="Transparent" HorizontalAlignment="Right" Width="188"/>
</muxc:TabView.TabStripFooter>
</muxc:TabView>
</Grid>
The MainPage.xaml.cs
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
Tabs.SelectedIndex = 0;
// Extend into the titlebar
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.ExtendViewIntoTitleBar = true;
coreTitleBar.LayoutMetricsChanged += CoreTitleBar_LayoutMetricsChanged;
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
titleBar.ButtonBackgroundColor = Windows.UI.Colors.Transparent;
titleBar.ButtonInactiveBackgroundColor = Windows.UI.Colors.Transparent;
Window.Current.SetTitleBar(CustomDragRegion);
// Main Window - add some tabs
for (int i = 0; i < 24; i++)
{
Tabs.TabItems.Add(CreateNewTVI($"Item {i}", $"Page {i}"));
}
}
private void CoreTitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
{
// To ensure that the tabs in the titlebar are not occluded by shell
// content, we must ensure that we account for left and right overlays.
// In LTR layouts, the right inset includes the caption buttons and the
// drag region, which is flipped in RTL.
// The SystemOverlayLeftInset and SystemOverlayRightInset values are
// in terms of physical left and right. Therefore, we need to flip
// then when our flow direction is RTL.
if (FlowDirection == FlowDirection.LeftToRight)
{
CustomDragRegion.MinWidth = sender.SystemOverlayRightInset;
ShellTitlebarInset.MinWidth = sender.SystemOverlayLeftInset;
}
else
{
CustomDragRegion.MinWidth = sender.SystemOverlayLeftInset;
ShellTitlebarInset.MinWidth = sender.SystemOverlayRightInset;
}
// Ensure that the height of the custom regions are the same as the titlebar.
CustomDragRegion.Height = ShellTitlebarInset.Height = sender.Height;
}
private TabViewItem CreateNewTVI(string header, string dataContext)
{
var newTab = new TabViewItem()
{
IconSource = new Microsoft.UI.Xaml.Controls.SymbolIconSource()
{
Symbol = Symbol.Placeholder
},
Header = header,
Content = new TextBlock()
{
Text = "This is a text:\n" + dataContext, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
FontSize = 25,
}
};
return newTab;
}
To fix this, you have to first add a Loaded event to your CustomDragRegion. Now you can remove the code from the OnNavigateTo function and paste it into the Loaded function. That's it.
private void CustomDragRegion_Loaded(object sender, RoutedEventArgs e)
{
Tabs.SelectedIndex = 0;
// Extend into the titlebar
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.ExtendViewIntoTitleBar = true;
coreTitleBar.LayoutMetricsChanged += CoreTitleBar_LayoutMetricsChanged;
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
titleBar.ButtonBackgroundColor = Windows.UI.Colors.Transparent;
titleBar.ButtonInactiveBackgroundColor = Windows.UI.Colors.Transparent;
Window.Current.SetTitleBar(CustomDragRegion);
// Main Window - add some tabs
for (int i = 0; i < 24; i++)
{
Tabs.TabItems.Add(CreateNewTVI($"Item {i}", $"Page {i}"));
}
}

Printing Canvas using PrintDialog in WPF

I am trying to take the print of Canvas in A4 size by setting height and width to 29.7cm and 21cm respectively.
I am getting proper output on Windows 7 devices. But for Windows 10 devices, the print output is not same as Windows 7 devices.
The real problem is height and width is not maintained for Windows 10 devices.
Please Help and Suggest Me !!
Please find below code:
Xaml:
<Window x:Class="TestSolution.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Height="29.7cm"
Width="21cm"
ResizeMode="NoResize">
<Grid x:Name="grdTest"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Height="29.7cm"
Width="21cm">
<Canvas x:Name="cnvTest"
VerticalAlignment="Stretch"
Height="29.7cm"
Width="21cm"
HorizontalAlignment="Stretch">
</Canvas>
</Grid>
</window>
Code Behind
private void btn_Click(object sender, RoutedEventArgs e)
{
PrintDialog prnt = new PrintDialog();
if (prnt.ShowDialog() == true)
{
prnt.PrintVisual(cnvTest, "Printing Canvas");
}
this.Close();
}
Finally got the solution. courtesy Magnus (MM8)
Please find solution on the below link
https://social.msdn.microsoft.com/Forums/en-US/b26c7898-169a-4726-b582-e79dc63f8450/printing-canvas-using-printdialog-in-wpf?forum=wpf
Here is a copy of the answer from the site, assuming a canvas with the name cnvTest:
private void btn_Click(object sender, RoutedEventArgs e)
{
PrintDialog prnt = new PrintDialog();
if (prnt.ShowDialog() == true)
{
Size pageSize = new Size(prnt.PrintableAreaWidth, prnt.PrintableAreaHeight);
cnvTest.Measure(pageSize);
cnvTest.Arrange(new Rect(5, 5, pageSize.Width, pageSize.Height));
if (prnt.ShowDialog() == true)
{
prnt.PrintVisual(canvas, "Printing Canvas");
}
}
this.Close();
}
you can try this code
System.Windows.Controls.PrintDialog printDialog = new System.Windows.Controls.PrintDialog();
System.Drawing.Printing.PrinterSettings printer = new System.Drawing.Printing.PrinterSettings();
System.Printing.LocalPrintServer localPrintServer = new System.Printing.LocalPrintServer();
System.Printing.PrintTicket pt = new System.Printing.PrintTicket();
System.Printing.PrintQueue pq = new System.Printing.PrintQueue(localPrintServer, printer.PrinterName, System.Printing.PrintSystemDesiredAccess.AdministratePrinter);
System.Printing.PageMediaSize PMS = new System.Printing.PageMediaSize(canvas.ActualWidth + 20, canvas.ActualHeight + 20);
System.Windows.Size pageSize = new System.Windows.Size(canvas.ActualWidth+20, canvas.ActualHeight+20);
canvas.Arrange(new Rect(0, 0, pageSize.Width, pageSize.Height));
canvas.Measure(pageSize);
pt.PageMediaSize = PMS;
pt.PageMediaType = System.Printing.PageMediaType.Unknown;
pq.DefaultPrintTicket.PageMediaSize = PMS;
pq.DefaultPrintTicket.PageMediaType = System.Printing.PageMediaType.Unknown;
printDialog.PrintQueue = pq;
printDialog.PrintTicket = pt;
printDialog.PrintQueue.Commit();
if (printDialog.ShowDialog() == true)
{
printDialog.PrintVisual(canvas, "Test");
}

How to center MDI child Form?

I have this code, to add a new child:
private void Menu1_Click(object sender, RoutedEventArgs e)
{
Point centerPoint = new Point((Container.ActualWidth / 2) - (500/2), (Container.ActualHeight / 2) - (400 / 2));
MdiChild newForm = new MdiChild();
newForm.Title = "My Form";
newForm.Content = new MyForm1();
newForm.Width = 500;
newForm.Height = 400;
newForm.Resizable = false;
newForm.MaximizeBox = false;
newForm.Position = centerPoint;
Container.Children.Add(newForm);
}
This code is for the view:
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
xmlns:mdi="clr-namespace:WPF.MDI;assembly=WPF.MDI"
Title="WPF.MDI Example" Height="362" Width="684" WindowState="Maximized">
<DockPanel>
<mdi:MdiContainer Name="Container" Background="#FF474040" >
<mdi:MdiContainer.Menu>
<Menu DockPanel.Dock="Top" Width="677">
<MenuItem Header="Menu1" click="Menu1_Click">
</mdi:MdiContainer.Menu>
</mdi:MdiContainer>
</DockPanel>
How can I center MDI child Form?
It always opens in the upper left side
I don't find the solution yet,
Thanks.
I also tried to put this code, in the code behind form child, but it doesn't works:
public partial class MyForm1: UserControl
{
BalanceEntities db = new BalanceEntities();
public MyForm1()
{
InitializeComponent();
this.HorizontalAlignment = HorizontalAlignment.Center;
this.VerticalAlignment = VerticalAlignment.Center;
}
}
You should use WindowStartupLocation property of the Window like this:
private void Menu1_Click(object sender, RoutedEventArgs e)
{
Point centerPoint = new Point((Container.ActualWidth / 2) - (500/2), (Container.ActualHeight / 2) - (400 / 2));
MdiChild newForm = new MdiChild();
newForm .Title = "My Form";
//The code omitted for the brevity
newForm.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
Container.Children.Add(newForm);
}
OR:
this.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
Thanks for James Thorpe, I solve my problem at this way:
private void Menu1_Click(object sender, RoutedEventArgs e)
{
MdiChild newForm = new MdiChild();
newForm.Title = "My Form";
newForm.Content = new MyForm1();
newForm.Width = 500;
newForm.Height = 400;
newForm.Resizable = false;
newForm.MaximizeBox = false;
Container.Children.Add(newForm);
// And then I add the position:
Point centerPoint = new Point((Container.ActualWidth / 2) - (newForm.Width / 2), (Container.ActualHeight / 2) - (newForm.Height / 2));
newForm.Position = centerPoint;
}
Thanks for the help.
Do the following , it worked for me
newForm.StartPosition = FormStartPosition.CenterScreen;

WPF. How to get a DrawingContext for a TextBlock?

I have a TextBlock. I want to draw inside it (within its current visual boundaries). I need DrawingContext for it.
How do I get DrawingContext?
(MSDN says that any FrameworkElement is descendant from Visual and the Visual provides support for rendering.. But I cant quite find how exactly)
Please note - this code will be called several time a second. I am looking for the most efficient solution (this is why I took on the DrawingContext in first place).
what I did: I wrote my own adorner, adorned the textblock, used OnRender to get DrawingContext, I am writing text directly to the adorner and invalidating Visual at each change.
The boost in performance (measured carefully with Ants profiler) is 4.5 times better than writing text directly to the text block and even more so than binding string property.
Why not overlay the TextBlock on a Canvas and draw to the Canvas ?
XAML
<Grid>
<Canvas Background='Orange'
x:Name='drawingCanvas'
Width='{Binding ActualWidth, ElementName=textblock1, Mode=OneWay}'
Height='{Binding ActualHeight, ElementName=textblock1, Mode=OneWay}' />
<TextBlock Text='Example'
x:Name='textblock1'
HorizontalAlignment='Center'
VerticalAlignment='Center'
FontSize='40' />
</Grid>
CODE
public MainWindow() {
InitializeComponent();
this.Loaded += MainWindow_Loaded;
_timer.Interval = TimeSpan.FromMilliseconds(100);
_timer.Start();
_timer.Tick += _timer_Tick;
}
private void _timer_Tick(object sender, EventArgs e) {
var newX = _bezierSeg.Point1.X + .1;
_bezierSeg.Point1 = new Point(Math.Sin(newX) * 12, 0);
}
private DispatcherTimer _timer = new DispatcherTimer();
private BezierSegment _bezierSeg = new BezierSegment();
private void MainWindow_Loaded(object sender, RoutedEventArgs e) {
var arcPath = new Path();
var figure = new PathFigure();
figure.StartPoint = new Point(0, 0);
var Point1 = new Point(textblock1.ActualHeight, 0);
var Point2 = new Point(textblock1.ActualWidth - 30, textblock1.ActualHeight - 20);
var Point3 = new Point(textblock1.ActualWidth, textblock1.ActualHeight);
_bezierSeg.Point1 = Point1;
_bezierSeg.Point2 = Point2;
_bezierSeg.Point3 = Point3;
var myPathSegmentCollection = new PathSegmentCollection();
myPathSegmentCollection.Add(_bezierSeg);
figure.Segments = myPathSegmentCollection;
var pathCollection = new PathFigureCollection();
pathCollection.Add(figure);
var pathGeometry = new PathGeometry();
pathGeometry.Figures = pathCollection;
arcPath.Stroke = new SolidColorBrush(Colors.Red);
arcPath.Fill = new SolidColorBrush(Colors.Yellow);
arcPath.StrokeThickness = 2;
arcPath.Data = pathGeometry;
drawingCanvas.Children.Add(arcPath);
}

Rotate a WPF Canvas derived component using code behind

I'm wanting to make a component that inherits from rotate canvas using a storyboard. But when I try nothing happens.
Eventually I also want to dynamically change the speed of rotation to a stop. But first, i need rotate the componente.
This is the code for the component:
class MyToy : Canvas
{
public MyToy()
{
this.Background = System.Windows.Media.Brushes.Green;
this.Width = 300;
this.Height = 300;
Polyline poly = new Polyline();
poly.Points.Add(new Point(25, 25));
poly.Points.Add(new Point(0, 50));
poly.Points.Add(new Point(25, 75));
poly.Points.Add(new Point(50, 50));
poly.Points.Add(new Point(25, 25));
poly.Points.Add(new Point(25, 0));
poly.Stroke = System.Windows.Media.Brushes.Blue;
poly.StrokeThickness = 10;
this.Children.Add(poly);
Canvas.SetLeft(poly, 120);
Canvas.SetTop(poly, 120);
}
}
the window xaml code is:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="500" Width="500">
<Canvas x:Name="myCanvas">
<Button Canvas.Left="0" Canvas.Top="0" Content="Rotate" Height="23" Name="button1" Width="111" Click="button1_Click" />
</Canvas>
</Window>
And finaly, the code behind, where i create the storyboard is:
public partial class Window1 : Window
{
MyToy myToy;
RotateTransform transform;
public Window1()
{
InitializeComponent();
myToy = new MyToy();
transform = new RotateTransform();
// transform.Name = "MyToy1Transform";
myToy.RenderTransform = transform;
// this.RegisterName(transform.Name, transform);
myToy.Name = "MyToy1";
this.RegisterName("MyToy1", myToy);
myCanvas.Children.Add(myToy);
Canvas.SetTop(myToy, 50);
Canvas.SetLeft(myToy, 50);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
DoubleAnimation ani = new DoubleAnimation();
ani.From = 0;
ani.To = 359;
ani.AutoReverse = true;
ani.RepeatBehavior = RepeatBehavior.Forever;
ani.Duration = new Duration(TimeSpan.FromSeconds(2));
Storyboard story = new Storyboard();
story.Children.Add(ani);
// Storyboard.SetTargetName(ani, myToy.Name);
Storyboard.SetTarget(ani, transform);
Storyboard.SetTargetProperty(ani, new PropertyPath(RotateTransform.AngleProperty));
story.Begin(this);
}
}
thanks for any help.
The target of your animation is a MyToy object and your target property is Angle. MyToy doesn't have an angle property though. Solution: Set the RenderTransform (or LayoutTransform) property of MyToy to be a new RotateTransform object. Then use that object (which has the Angle property) as the target of the animation.

Categories

Resources