I've been working on drawing a grid in C#, which works! But somehow I keep running into some problems with the method DrawGrid.
I found that it is being redrawn every time a label gets added, and continuously after that. Here is my code:
Rectangle[,] rec = new Rectangle[6,6];
Label label_1 = new Label();
Label label_2 = new Label();
public Reversi()
{
ClientSize = new Size(500,425);
BackColor = Color.DarkGreen;
NewGame();
Paint += DrawGrid;
}
public void NewGame()
{
// here is some more stuff which I will leave out for the sake of clarity
for (int i = 0; i < grspel; i++)
{
for (int j = 0; j < grspel; j++)
{
rec[i, j] = new Rectangle(50 + i * 50, 100 + j * 50, 50, 50);
}
}
DrawLabels();
}
public void DrawLabels()
{
label_1.Location = new Point(20, 50);
label_1.Text = "Zwart: " + zwart;
Controls.Add(label_1);
label_2.Location = new Point(330, 50);
label_2.Text = "Wit: " + wit;
Controls.Add(label_2);
}
public void DrawGrid(object o, PaintEventArgs pea)
{
MessageBox.Show("test");
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
pea.Graphics.DrawRectangle(new Pen(Brushes.Black), rec[i,j]);
}
}
}
To visualize my problem I placed MessageBox.Show("test"); at the start of my DrawGrid method. The rest of my code (which is not on here) uses this method and is not working properly. I've narrowed the problem down to this.
Getting rid of the Controls.Add(label_i) fixes the problem, but I need the labels.
Why does this happen and more importantly, how can I fix it?
You add a label, thus there's an event that UI changes and needs to repaint. It should paint the added label, right? IMHO that's expected.
You don't need to add the labels to the list of controls each time. Just add them once (e.g. in Form.Load).
However, changing the position of the label will likely also require a repaint. It should paint the label at the new position, right? But your position is constant as well. Just set the position once.
Repainting is normal and good, because it keeps the contents updated. It shouldn't be a problem. So, why don't you like it? Probably it causes flickering and you don't like that.
Showing a MessageBox might not be a good idea either. If the MessageBox is in front of your form, the part that is behind the MessageBox will need to be painted again.
You can
use SuspendLayout() to tell the painter that it's not worth reacting to changes immediately, but wait until you finished making all layout related changes. Use ResumeLayout() to tell the painter it's time to paint now.
check out options for double buffering
but I need the labels
Actually you don't. You can use Graphics.DrawString() instead of using a label.
Related
Good day!
I have read a lot about dynamic cells back color changing, but have not found the resolve of my problem.
In my project, I used 32x32 DataGridView. All values are integer, also I've changed default cell format to Numeric.
This code makes gradient painting without any problem:
void AFRTableGraient()
{
for (int i = 0; i < dataGridViewTAF.ColumnCount; i++)
{
for (int j = 0; j < dataGridViewTAF.RowCount; j++)
{
try
{
if (dataGridViewTAF.Rows[i].Cells[j].Value == null)
{
break;
}
dataGridViewTAF.Rows[i].Cells[j].Style.BackColor = Color.FromArgb(0x10 + (int)dataGridViewTAF.Rows[i].Cells[j].Value, 0x88 + (int)dataGridViewTAF.Rows[i].Cells[j].Value, 0x88 + (int)dataGridViewTAF.Rows[i].Cells[j].Value);
}
catch
{
break;
}
}
}
dataGridViewTAF.Update();
dataGridViewTAF.Refresh();
}
But I need to change cell color after value changed.
I've used CellFormatting, KeyDown, CellLeave, CellPainting, Enter, etc, events, but has no result.
When I tried to use CellPainting method, the app is freeze, but update the color is not happening.
Also, I've tried to use the "timer", but still nothing.
I know, there is a simple way of resolving, but I need help to found it.
Thanks!
So I have this idea of adding several buttons to a grid, then arranging them, and the number of buttons added is related to a number I enter. All works fine, until I try to make it work under MVVM.
Code that works in the MainWindow.xaml.cs:
private void Button_Click(object sender, RoutedEventArgs e)
{
int i = 0, j = 0;
Brush[] bs = { Brushes.BurlyWood, Brushes.Honeydew };
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
Button btn = new Button();
btn.Height = 50;
btn.Width=50;
btn.SetValue(Grid.RowProperty, i);
btn.SetValue(Grid.ColumnProperty, j);
if ((i + j) % 2 == 0)
{
btn.Background=bs[0];
}
else btn.Background = bs[1];
GameGrid.Children.Add(btn);
}
}
}
So basically, I press a button and it adds 3x3 buttons, colored nicely and spread appropriately. But then I get to the MVVM:
private void ButtonClickCommand()
{
RowCount = GridNumber;
ColumnCount=GridNumber;
int i = 0, j = 0;
Brush[] bs = { Brushes.BurlyWood, Brushes.Honeydew };
for (i = 0; i < GridNumber; i++)
{
for (j = 0; j < GridNumber; j++)
{
Button btn = new Button();
btn.Height = 50;
btn.Width = 50;
// btn.Command = StartCommand; Ill add that later
btn.SetValue(Grid.RowProperty, i);
btn.SetValue(Grid.ColumnProperty, j);
if ((i + j) % 2 == 0)
{
btn.Background = bs[0];
}
else btn.Background = bs[1];
somebuttonlist.Add(btn);
}
}
}
Here I have a List of buttons, which should accept the new created buttons, then transfer them to the grid. Code of the button list:
private List<Button> _bslist = new List<Button>();
public List<Button> somebuttonlist
{
get
{
return _bslist;
}
set
{
_bslist = value;
NotifyPropertyChanged();
}
}
And xaml code:
<ItemsControl ItemsSource="{Binding somebuttonlist}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Name="GameGrid" Visibility="{Binding GameVis}"
vm:GridHelpers.RowCount="{Binding RowCount}"
vm:GridHelpers.ColumnCount="{Binding ColumnCount}">
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Don't mind all the bindings, they work properly when I use the non-MVVM format.
So how do I properly transfer the working code into the View Model? Is there a way to access the children accordingly?
It looks like you are binding to a List, which does not notify when new records are inserted or any record is deleted. The NotifyPropertyChanged() that you have only kicks off when the reference to that list is modified, so for example, setting it to a new List() or a different List. Try changing it to an ObservableCollection instead, which sends an event for when the number of records in the collection get modified.
This is not the right way of doing things with MVVM. What you are trying to do now is to pull visual code into your ViewModel. Ideally, ViewModels should leave in a separate project without any references to visual stuff. This way, referencing Button will result in broken code unless you add a reference for System.Windows.Controls and this reminds you that you do things the wrong way.
What you are trying to do is doable in pure MVVM way, but will result in much more code, which also be convoluted to you if you haven't got a grasp on MVVM yet. Also note, that in real life, it is acceptable to have some code behind for complicated scenarios. For this you just create a custom control to encapsulate all code behind logic.
The nice solution in your case can be a custom control, that encapsulates Grid and logic to populate it with buttons. You also add dependency properties for Rows and Columns, so you can bind to them in XAML, and define a command which is invoked when button is clicked. This may be a good example: How to create bindable commands in Custom control?
I have a TableLayoutPanel that has a dynamic amount of columns and rows determined by the user. I want the buttons inside to be square and the same size, but whenever I use a loop to set the column/rows styles, they never turn out to be the size I want them to be.
How can I get the column/row styles to set the appropriate widths and height os the container elements?
Here is the loop method of the code that handles setting the width size of the table (I use a similar method for rows)
void FormatTableWidth(ref TableLayoutPanel container)
{
TableLayoutColumnStyleCollection columnStyles = container.ColumnStyles;
foreach (ColumnStyle style in columnStyles)
{
style.SizeType = SizeType.Absolute;
style.Width = 60;
}
}
You can do it like....
public void AddButtontControls()
{
tblPanel.SuspendLayout();
tblPanel.Controls.Clear();
tblPanel.GrowStyle = TableLayoutPanelGrowStyle.FixedSize;//.AddColumns;
tblPanel.ColumnStyles.Clear();
for (int i = 0; i < tblPanel.ColumnCount; i++)
{
ColumnStyle cs = new ColumnStyle(SizeType.Percent, 100 / tblPanel.ColumnCount);
tblPanel.ColumnStyles.Add(cs);
//Add Button
Button a = new Button();
a.Text = "Button " + i + 1;
tblPanel.Controls.Add(a);
}
tblPanel.ResumeLayout();
}
Sorry to tell you, but you don't use the right control.
You definitely must use FlowLayoutPanel control, and you can add as many controls you want in it, you can tell which direction will fill the control, if it wrap content or not, and many others.
And the most important - It Will Not Flickering like TableLayoutPanel does :)
First of all, I have to mention that i'm a newbie at c#. I'm developing an windows 8 metro app with xaml and c#.
I created an array of checkboxes dynamically and assigned a pointerpressed event to each of them.
the weird problem is that the pointerpressed event is not firing up the registerd function.
If i create the same checkboxes using design tool(I'm using visual Studio 2012), strangely the function does work this time.
As i don't know the number of checkboxes to be created till runtime, using design tool for handling checkboxes isn't an option.The code i'm running is as below:
CheckBox[] chk=new CheckBox[count];
int x=20; //Width of each checkbox(to be incremented for every iteration)
for( int i = 0; i < count ; i++ )
{
chk[i] = new CheckBox();
chk[i].HorizontalAlignment = HorizontalAlignment.Left;
chk[i].Margin = new Thickness(1100, x, 0, 0);
chk[i].VerticalAlignment = VerticalAlignment.Top;
chk[i].Height = 30;
chk[i].Width = 35;
chk[i].PointerPressed += new PointerEventHandler(chkhandler);
gridit.Children.Add(chk[i]);
x = x + 35;
}
private static void chkhandler(object obj, PointerRoutedEventArgs arg)
{
textblock1.Text="Testing"; //Sample Textblock
}
The checkboxes get checked during runtime but the associated function doesn't work.Thanks in advance
Try;
making the event handler public & non-static,
make sure nobody is eating up the events.
what about this:
chk[i].AddHandler(PointerPressedEvent,
new PointerEventHandler(SomeButton_PointerPressed), true);
I use this solution(in code below) to add multiply buttons on panel. It works ok but it takes too long, when it tries to add a lot of buttons (for an example 40). I want to ask, if any one knows of a better solution for this situation? I was thinking of creating all possible buttons at program start-up, but in this case will start-up take too long, especially if there will be really lot of buttons (this scenario is possible)?
while (data.Read())
{
btnName = Convert.ToString(data["Name"]);
btnColor = (color == string.Empty) ? Convert.ToString(data["Color"]) : color;
categoryId = Convert.ToInt16(data["CategoryId"]);
//both category and article table's contains this data!
if (categoryId == articleCatId || cl == typeof(Category))
{
Button newbtn = new Button();
newbtn.TextAlign = ContentAlignment.MiddleCenter;
newbtn.Click += (sender, e) => method(sender, e);
newbtn.Text = btnName;
newbtn.Name = "button-" + btnName;
newbtn.Height = size;
newbtn.Width = size;
newbtn.Font = new Font("Microsoft Sans Serif", fontH);
newbtn.Location = new Point(paddingL, paddingT);
newbtn.BackColor = ColorTranslator.FromHtml(btnColor);
location.Controls.Add(newbtn);
num += 1;
if ((num - 1) / inline == 1) { paddingT += size; paddingL = 2; num = 1; }
else { paddingL = paddingL + size; }
}
}
You probably cannot reduce the number of buttons you need to create, so you then have some options to speed it up a little bit:
Add the buttons to an object that is not visible. Only when you're done adding buttons, you make the object visible.
Call SuspendLayout on the parent control to stop it from trying to layout itself. Then call ResumeLayout when you're done adding buttons.
Use a more lightweight control than a button, that is more appropriate for the task. For example a Listbox, Combobox, or several checkboxes or option buttons styled as normal buttons.
Write your own lightweight Button control that does exactly what you want but no more.