Painting a UniformGrid in C# using WPF - c#

I am relatively new to C# and I'm trying to create a window with a grid of a dynamic amount of same-sized squares. The squares are then to have their color changed by a process.
I'm currently struggling to generate the grid of squares. Whenever I run the application it seems to be going crazy on resources and I'm not sure why.
The code I am using follows:
private void Window_Loaded(object sender, RoutedEventArgs e) {
//create a blue brush
SolidColorBrush vBrush = new SolidColorBrush(Colors.Blue);
//set the columns and rows to 100
cellularGrid.Columns = mCellAutomaton.GAME_COLUMNS;
cellularGrid.Rows = mCellAutomaton.GAME_ROWS;
//change the background of the cellular grid to yellow
cellularGrid.Background = Brushes.Yellow;
//create 100*100 blue rectangles to fill the cellular grid
for (int i = 0; i < mCellAutomaton.GAME_COLUMNS; i++) {
for (int j = 0; j < mCellAutomaton.GAME_ROWS; j++) {
Rectangle vRectangle = new Rectangle();
vRectangle.Width = 10;
vRectangle.Height = 10;
vRectangle.Fill = vBrush;
cellularGrid.Children.Add(vRectangle);
}
}
}
Is this even the approach I want to take if I want a modifiable grid of squares?
Thanks for any help,
Jason

this seems to perform pretty fast
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="350" Name="UI">
<Grid Name="Test">
<UniformGrid Name="UniGrid" />
</Grid>
</Window>
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
AddRows(new Size(10, 10));
}
private void AddRows(Size recSize)
{
UniGrid.Columns = (int)(UniGrid.ActualWidth / recSize.Width);
UniGrid.Rows = (int)(UniGrid.ActualHeight / recSize.Height);
for (int i = 0; i < UniGrid.Columns * UniGrid.Rows; i++)
{
UniGrid.Children.Add(new Rectangle { Fill = new SolidColorBrush(Colors.Yellow), Margin = new Thickness(1) });
}
}
}

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}"));
}
}

WPF Grid white stripes

I have a Grid with a lot of buttons inside. These buttons are supposed to be seamlessly connecting. In most of the cases, this is actually working, but sometimes there's a white stripe between the columns / rows of the grid:
I'm adding the buttons to the grid via the code behind like this (I'm sorry for non-minimal code, but I really don't know what might be relevant here):
public partial class MainWindow
{
private int _xCount;
private int _yCount;
public MainWindow ()
{
InitializeComponent ();
SetSize (40, 40);
}
public void SetSize (int x, int y)
{
_xCount = x;
_yCount = y;
Grid.ColumnDefinitions.Clear ();
Grid.RowDefinitions.Clear ();
for (var i = 0; i < x; i++)
Grid.ColumnDefinitions.Add (new ColumnDefinition {Width = new GridLength (100, GridUnitType.Star)});
for (var i = 0; i < y; i++)
Grid.RowDefinitions.Add (new RowDefinition {Height = new GridLength (100, GridUnitType.Star)});
for (var xI = 0; xI < x; xI++)
for (var yI = 0; yI < y; yI++)
{
var button = new Button
{
BorderThickness = new Thickness (1),
BorderBrush = Brushes.Gray,
Foreground = Brushes.DarkGray,
Content = "",
Background = Brushes.DarkGray
};
Grid.Children.Add (button);
Grid.SetColumn (button, xI);
Grid.SetRow (button, yI);
}
SetButtonSizes ();
}
private void SetButtonSizes ()
{
var gridWidth = Grid.Width;
var gridHeight = Grid.Height;
var buttonWidth = gridWidth / _xCount;
var buttonHeight = gridHeight / _yCount;
foreach (var button in Grid.Children)
{
((Button) button).Width = buttonWidth;
((Button) button).Height = buttonHeight;
}
}
protected override void OnRenderSizeChanged (SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged (sizeInfo);
SetButtonSizes ();
}
}
The WPF is pretty trivial and looks like that:
<Window x:Class="Minesweeper.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="1000"
Width="1000">
<Grid Name="Grid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Window>
I already tried Pixel Snapping, which didn't make any difference.
You should set UseLayoutRounding to true on the Grid and not programmatically resize the Buttons.
However, you could greatly simplify your code by using a UniformGrid
<Window ...>
<UniformGrid x:Name="grid"/>
</Window>
and add Buttons like this:
public MainWindow()
{
InitializeComponent();
SetSize(40, 40);
}
private void SetSize(int x, int y)
{
grid.Children.Clear();
grid.Columns = x;
for (int i = 0; i < x * y; i++)
{
grid.Children.Add(new Button
{
BorderThickness = new Thickness(1),
BorderBrush = Brushes.Gray,
Background = Brushes.DarkGray,
Foreground = Brushes.DarkGray,
Content = ""
});
}
}

WPF number of rectangle-array-element in ClickEvent needed

I'm currently porting a perl-tk-application to c# wpf. The application supplies a graphical interface with different timelines for different systems. The timelines consist of rectangles - each one representing a special event - that can be clicked for obtaining deeper information about the event. The rectangles have different sizes depending on the length of the event - so their sizes (width) are different and not predictable.
All I need now is the possibility to bind an event to each of the rectangles that - at least - lets me track back which rectangle was clicked. In Perl it was as easy as this:
$rectangle = $Canvas->create_rectangle( $x1, $y1 ,$x2 ,oy2 , -outline => "red", -fill => "red");
$Canvas->bind($rectangle, "<1>", sub {DoAction[$number]});
That means you can just put the event-binding after the element that needs to be clickable.
I've wasted the whole weekend in searching for a solution to do this in c# wpf... Important to know - I'm an absolut newbie in c#.
My code so far: I generated 10 rectangles via array. I want to pass the number of the rectangle-array to the ClickEvent. In the following example code the ClickEvent always prints out the highest index. I assume, there exists only one Event and I would need to generate a array of events...? What's the solution for this? In short words: Which rectangle (number) was clicked?
private void ClickEvent (object sender, EventArgs e, int i) {
var time = DateTime.Now;
string name = ((Shape)sender).Name;
Console.WriteLine("Rectangle click at " + time + " from " + name + " Rect. Nr." + i);
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
int NumObjects = 10;
Rectangle[] RectangleArray = new Rectangle[NumObjects];
for (int i = 0; i < NumObjects - 1; i++) {
RectangleArray[i] = new Rectangle();
RectangleArray[i].Width = 50;
RectangleArray[i].Height = 50;
RectangleArray[i].Fill = Brushes.Red;
Canvas.SetTop(RectangleArray[i], i * 50);
Canvas.SetLeft(RectangleArray[i], i * 50);
RectangleArray[i].MouseLeftButtonDown += (sender2, e2) => ClickEvent(sender2, e2, i);
Canvas1.Children.Add(RectangleArray[i]);
}
}
XAML:
<Window x:Class="WpfRectangleEvent.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:WpfRectangleEvent"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
Loaded="Window_Loaded">
<Grid>
<Canvas HorizontalAlignment="Left" Height="666" Margin="-17,-20,0,0"
VerticalAlignment="Top" Width="517"
Name="Canvas1" Grid.ColumnSpan="2">
</Canvas>
</Grid>
In the event handler method the sender object is the Rectangle which invokes the event.
So if you store the rectangles, you can find out the index of that rectangle.
OK! Got it, this works:
private void ClickEvent(object sender, EventArgs e, int i) {
var time = DateTime.Now;
Console.WriteLine("Rectangle click at " + time + " from Rect. Nr." + i);
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
int NumObjects = 10;
Rectangle[] RectangleArray = new Rectangle[NumObjects];
for (int i = 0; i < NumObjects - 1; i++) {
int index = i;
RectangleArray[i] = new Rectangle();
RectangleArray[i].Width = 50;
RectangleArray[i].Height = 50;
RectangleArray[i].Fill = Brushes.Red;
Canvas.SetTop(RectangleArray[i], i * 50);
Canvas.SetLeft(RectangleArray[i], i * 50);
RectangleArray[i].MouseLeftButtonDown += (sender2, e2) => ClickEvent(sender2, e2, index);
Canvas1.Children.Add(RectangleArray[i]);
}
}

C# WPF. How to add Ellipces dynamically into canvas?

I'm trying to add some ellipces with random positions into my canvas, but i can see them on my canvas. Progmab is compilling quite saccesfull. Code:
for (int i = 0; i < FirefliesCount; ++i)
{
Firefly CurrentFirefly = new Firefly();
CurrentFirefly.Speed = Randomer.Next(1, 3);
CurrentFirefly.Body = new Ellipse();
CurrentFirefly.Body.Margin = new Thickness(Randomer.Next(10, (int)MainCanvas.Width - 10),
Randomer.Next(10, (int)MainCanvas.Height - 10),
0, 0);
CurrentFirefly.Body.Fill = Brushes.Black;
CurrentFirefly.Body.Height = MainCanvas.Height / 4;
CurrentFirefly.Body.Width = 1.5 * CurrentFirefly.Body.Height;
MainCanvas.Children.Add(CurrentFirefly.Body);
}
And Fireflie class:
class Firefly
{
public Ellipse Body { get; set; }
public int Speed { get; set; }
}
Probably you did not set the Width and Height properties of your MainCanvas; then they have the value NaN and therefore you will not see the ellipses.
My suggestion is to use ActualWidth and ActualHeight instead and to delay the adding of the ellipses until the canvas is loaded. Here is an example:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainCanvas.Loaded += MainCanvas_Loaded;
}
void MainCanvas_Loaded(object sender, RoutedEventArgs e)
{
Init();
}
private void Init()
{
const int FirefliesCount = 100;
Random Randomer = new Random();
for (int i = 0; i < FirefliesCount; ++i)
{
Firefly CurrentFirefly = new Firefly();
CurrentFirefly.Speed = Randomer.Next(1, 3);
CurrentFirefly.Body = new Ellipse();
CurrentFirefly.Body.Margin = new Thickness(Randomer.Next(10, (int)MainCanvas.ActualWidth - 10),
Randomer.Next(10, (int)MainCanvas.ActualHeight - 10),
0, 0);
CurrentFirefly.Body.Fill = Brushes.Black;
CurrentFirefly.Body.Height = MainCanvas.ActualHeight / 4;
CurrentFirefly.Body.Width = 1.5 * CurrentFirefly.Body.Height;
MainCanvas.Children.Add(CurrentFirefly.Body);
}
}
}
The corresponding xaml file looks like this:
<Window x:Class="WpfApplication7.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">
<Canvas x:Name="MainCanvas"/>
</Window>

Adorner in WPF, isEnabled not working?

I'm using an adorner layer to make a grid (like, boxes across the entire screen) across my grid (WPF grid).
I want this to be shown ONLY when a checkbox is marked. However, when I bind the "IsEnabled" property, nothing happens. Even if I set the IsEnabled to false, the grid overlay is still shown!
This is how I do from my mainWindow.xaml, note the IsEnabled is set to false, but it is still showing up:
<Grid>
<!--Adornerdecorator is made to make sure the adorner is in the background, and not the foreground-->
<AdornerDecorator>
<View:GridWithRulerxaml IsEnabled="False" />
</AdornerDecorator>
<ItemsControl ItemsSource="{Binding Classes}"/>
<ItemsControl ItemsSource="{Binding Edges}"/>
</Grid>
this is the adorner usercontrol:
namespace UMLDesigner.View
{
/// <summary>
/// Interaction logic for GridWithRulerxaml.xaml
/// </summary>
public partial class GridWithRulerxaml : UserControl
{
public GridWithRulerxaml()
{
InitializeComponent();
//Loaded event is necessary as Adorner is null until control is shown.
Loaded += GridWithRulerxaml_Loaded;
}
void GridWithRulerxaml_Loaded(object sender, RoutedEventArgs e)
{
var adornerLayer = AdornerLayer.GetAdornerLayer(this);
var rulerAdorner = new AlignmentAdorner(this);
adornerLayer.Add(rulerAdorner);
}
}
}
And finally the adorner itself:
namespace UMLDesigner.Utilities
{
public class AlignmentAdorner : Adorner
{
private FrameworkElement element;
public AlignmentAdorner(UIElement el)
: base(el)
{
element = el as FrameworkElement;
}
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
base.OnRender(drawingContext);
double height = element.ActualHeight;
double width = element.ActualWidth;
double linesHorizontal = height / 50;
double linesVertical = width / 50;
var pen = new Pen(Brushes.LightGray, 1) { StartLineCap = PenLineCap.Triangle, EndLineCap = PenLineCap.Triangle };
int offset = 0;
for (int i = 0; i <= linesVertical; ++i)
{
offset = offset + 50;
drawingContext.DrawLine(pen, new Point(offset, 0), new Point(offset, height));
}
offset = 0;
for (int i = 0; i <= linesHorizontal; ++i)
{
offset = offset + 50;
drawingContext.DrawLine(pen, new Point(0, offset), new Point(width, offset));
}
}
}
}
I really hope you can help me out here guys.
The IsEnabled property only enables/disables interaction with the element, and has nothing to do with the Visibility.
What you should do is set the Visibility property of the GridWithRulerxaml to Collapsed or Hidden when you want to hide it, and set it to Visible when you want it shown.
Edit: Tested it, setting the visiblity to Hidden of the GridWithRulerxaml usercontrol inside the AdornerDecorator doesn't hide the adorner.
And thinking some more about it, if this isn't what you want, it might be possible to do this with the IsEnabled property, watching for changes in it and adding/removing the adorner depending on the value.

Categories

Resources