I started making a WinForms application to display a grid of 4×4×4 checkboxes, representing a real-life 43 grid of LEDs.
This is what it looks like right now:
I want to convert this to an WPF app, so that I can use the transparency option of the WPF Checkbox, to make the non-selected layer [there will be 4 layers] of checkboxes slightly transparent, to give it a more true 3D feeling.
I am new to WPF and I have tried to nest 2 elements in the main Window [for example, 2 grids to slightly overlap the position, by maybe 20 pixels, so that they would appear to be 3d-staggered], but it simply won't let me do that, and only lets me add a child grid inside the original grid.
TLDR: How can I dynamically create lots of checkboxes while giving them absolute pixel positions?
My current working WinForms C# code:
int spacing = 25;
int zero = 0;
for (int z = 1; z <= 4; z++)
{
List<string> zString = new List<string>();
for (int y = 1; y <= 4; y++)
{
for (int x = 1; x <= 4; x++)
{
int pixel_x = zero + ((x - 1) * spacing);
int pixel_y = (zero - 4) + ((y - 1) * spacing);
//int id = ((y - 1) * 4) + x;
CheckBox box = new CheckBox();
box.CheckStateChanged += new System.EventHandler(checkBox2_CheckedChanged);
box.Tag = id;
zString.Add(id.ToString());
//box.Text = id.ToString();
box.BackColor = Color.Transparent;
//box.
box.AutoSize = false;
box.Size = new Size(20, 20);
box.Padding = new Padding(3);
box.Location = new Point(pixel_x, pixel_y);
this.Controls.Add(box);
id++;
}
}
zero += 25;
}
Consider using an ItemTemplate ( or DataTemplate) to establish how each item will look within your viewmodel's collection.
Example:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I would use multiple UniformGrids stacked on top of each other inside a regular grid (so they overlap). Each UniformGrid would have its Top and Left margins offset by a specific amount.
The item source of each UniformGrid would be bound to a list of Boolean properties on your view model for each "layer".
You can try this. Use a Canvas inside your Window.
<Window x:Name="myWindow1" x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Canvas x:Name="canvas" >
</Canvas>
Unlike Location available in Windows Forms, you can use Margin to set the position
int spacing = 100;
int zero = 0;
for (int z = 1; z <= 4; z++)
{
List<string> zString = new List<string>();
for (int y = 1; y <= 4; y++)
{
for (int x = 1; x <= 4; x++)
{
int pixel_x = zero + ((x - 1) * spacing);
int pixel_y = zero + ((y - 1) * spacing);
CheckBox box = new CheckBox();
Canvas.SetLeft(box, pixel_x);
Canvas.SetTop(box, pixel_y);
this.canvas.Children.Add(box);
}
}
zero = zero + 50;
}
Related
I am trying to plot my data using OxyPlot. I want to plot two lines in my graph but I am not sure how to provide the plot with data.
This is how my XAML file looks like:
<oxy:Plot Name="Plot1">
<oxy:Plot.Series>
<oxy:LineSeries ItemsSource="{Binding Graph1}"/>
<oxy:LineSeries ItemsSource="{Binding Graph2}"/>
</oxy:Plot.Series>
</oxy:Plot>
My question is how can I plot two lines in the same graph while using LineSeries?
Usually you don't add the LineSeries directly to a PlotView but to a PlotModel which is then bound to the Plot View.
The C# code could look like this:
PlotModel pm = new PlotModel();
var s1 = new LineSeries();
for (int i = 0; i < 1000; i++)
{
double x = Math.PI * 10 * i / (1000 - 1);
s1.Points.Add(new DataPoint(x, Math.Sin(x)));
}
pm.Series.Add(s1);
var s2 = new LineSeries();
for (int i = 0; i < 1000; i++)
{
double x = Math.PI * 10 * i / (1000 - 1);
s2.Points.Add(new DataPoint(x, Math.Cos(x)));
}
pm.Series.Add(s2);
Plot1.Model = pm;
The binding to Plot1 can of course also be done in XAML. If your DataContext provides the PlotModel via a property 'MyModel', it would look like this:
<oxyplot:PlotView Model="{Binding MyModel}"></oxyplot:PlotView>
I have a 2D array that contains rectangles. They are stored in a dynamic UniformGrid. How do I find out which rectangle in the array was MouseDown?
I tried this but I keep getting [0,0]:
if (e.OriginalSource is Shape s)
{
cellRow = (int)s.GetValue(Grid.RowProperty);
cellColumn = (int)s.GetValue(Grid.ColumnProperty);
rectangle[cellRow,cellColumn].Fill = new SolidColorBrush(Colors.Black);
}
And this is how I generated the rectangles:
rectangle = new Rectangle[rowcol, rowcol];
for (int x = 0; x < rowcol; x++)
{
for (int y = 0; y < rowcol; y++)
{
rectangle[y, x] = new Rectangle { Stroke = Brushes.Black, StrokeThickness = 0.5, Fill = Brushes.White };
GameUniformGrid.Children.Add(rectangle[y, x]);
}
}
The MouseDown event in xaml:
<UniformGrid x:Name="GameUniformGrid" HorizontalAlignment="Left" Height="272" VerticalAlignment="Top" Width="272" Grid.Row="0" Grid.Column="0" Rectangle.MouseDown="ToggleGrid"/>
I think the problem is with initializing a rectangle,
When you work with multidimentional array you have some rows and column and what you are doing is using row only in both row,column.
What if rowcol =0 ? the loop will not run because this condition.
x < rowcol (0<0);
You should
rectangle = new Rectangle[row, col];
for (int x = 0; x < row; x++)
{
for (int y = 0; y < col; y++)
{
rectangle[y, x] = new Rectangle { Stroke = Brushes.Black, StrokeThickness = 0.5, Fill = Brushes.White };
GameUniformGrid.Children.Add(rectangle[y, x]);
}
}
(For example)
where row = 2 ,col =2
I create a simple 3D viewer with Helix toolkit.
I want to plot 3D points which is colored.
I referred to the sample project "SimpleDemo" which is contained in the Example folder (HelixToolkit.Wpf.SharpDX).
This is my XAML:
<hx:PointGeometryModel3D x:Name="points"
Geometry="{Binding Points}"
Transform="{Binding Model1Transform}"
Color="{x:Static sdx:Color.White}" />
And drawing core is below.
var points = new PointGeometry3D();
var col = new Color4Collection();
var ptPos = new Vector3Collection();
var ptIdx = new IntCollection();
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
if(depth[y * width + x] < 1000 && depth[y * width + x] > 0) {
ptIdx.Add(ptPos.Count);
ptPos.Add(new Vector3(x, height - y, (-depth[y * width + x] / 3.0f) + 800));
col.Add(rnd.NextColor().ToColor4());
}
}
}
points.Positions = ptPos;
points.Indices = ptIdx;
points.Colors = col;
Points = points;
This program, Some cases are good work.(ex. deal with 200 x 200 points).
But other case are not good (ex. deal with 500 x 300 points)
It can draw 3D point which is not colored (black).
Why does it not work properly?
Comments:
A good work image (colored):
A not good work image (not colored):
I have a WPF project where i had to plot some lines in the project. But when i resize the window, the lines wouldn't resize because i using the canvas coordinate to plot the line. Anyone show me how to make the line resize vary to the window size?
my code for the line:
public static void drawGridLines(MainWindow main)
{
double axisX = 10;
Line lastLine = new Line();
lastLine.X2 = axisX;
lastLine.Y2 = 15;
double y = 0;
double x = 0;
bool first = true;
int[] point = new int[10] { 1, 3, 8, 9, 9, 0, 7, 5, 4, 1 };
for (int i = 0; i < point.Length; i++) // iterate over your gridview rows
{
Line newline = new Line();
newline.X1 = lastLine.X2;
newline.Y1 = lastLine.Y2;
newline.X2 = axisX + (Point[i] * 5); // calculate X position of the current cell
newline.Y2 = lastLine.Y2 + 10; // calculate Y position of the current cell
x = newline.X2;
y = newline.Y2;
if (!first)
{
// first minimum cell should't be drawn, it is just the start point for next line
drawLine(main, newline);
}
else
{
first = false;
}
lastLine = newline;
}
public static void drawLine(MainWindow main, Line line)
{
line.HorizontalAlignment = HorizontalAlignment.Left;
line.VerticalAlignment = VerticalAlignment.Center;
line.Stroke = System.Windows.Media.Brushes.SteelBlue;
line.StrokeThickness = 1.5;
main.myLineCanvas.Children.Add(line);
}
Place your Canvas in a Viewbox:
<Viewbox>
<Canvas x:Name="myLineCanvas" />
</Viewbox>
You can change its behavior with Stretch and StretchDirection.
You should try using a ViewBox and see if it works well in your case, is as simple as surrounding your canvas with the ViewBox.
<Window ...
...>
<ViewBox>
<Canvas .....>
</Canvas>
</ViewBox>
</Window>
Window has a SizeChanged event. In your handler you can grab the new size of your window and set the endpoints of your lines accordingly.
I'm trying to create 81 picture boxes and have them automatically positioned a certain distance apart from one another but they don't seem to be placing in any logical order. I have to initialize the X point to -1700 for them to even appear on the screen. The following code gets the first 15 where I want them but then they start stacking on top of one another instead of continuing the pattern. This is the result of about an hour of tinkering but initially the logic looked fine. I even had a message box that would display the current X,Y that was being set and it was correct it just would not place them at those coordinates.
int X = -1700;
int Y = 0;
for (int i = 0; i < 81; i++)
{
this.Controls.Add(championThumbNailsArray[i]);
championThumbNailsArray[i].Height = 80;
championThumbNailsArray[i].Width = 80;
championThumbNailsArray[i].Location = new Point(X, Y);
// MessageBox.Show(Convert.ToString(X) + "," + Convert.ToString(Y));
championThumbNailsArray[i].ImageLocation = akali.grabPicture();
//championThumbNailsArray[i].ImageLocation = championsArray[i].grabPicture();
if (X <= 425)
X = X + 85;
else
{
X = -1700;
Y = Y + 85;
}
}
Instead of manually placing elements use a FlowLayoutPanel. Add the controls to the panel and let it do the arrangement for you.
This code works as you are expecting
private void Form1_Load(object sender, EventArgs e)
{
int x = 0;
int y = 0;
for (int i = 0; i < 81; i++)
{
PictureBox p = new PictureBox();
p.BorderStyle = BorderStyle.Fixed3D;
p.Height = 80;
p.Width = 80;
p.Location = new Point(x, y);
x += 85;
if (x > 425)
{
x = 0;
y += 85;
}
this.Controls.Add(p);
}
}
But I would go with something like #Ed said, a FlowLayout control