how to avoid rebinding grid each time the control is initialized? - c#

I've created a custom user control with a grid. I'd like to bind this grid once, and use it over and over again in my app. If I put the binding within the control, the data is retrieved as many times as I use the control. How do I bind it only once??
public ClientLookUp()
{
InitializeComponent();
vw_clientsTableAdapter.Fill(dsclientlkup.vw_clients); //This occurs as many times as I have the user control, instead of just once.
}

Well anything you put in the constructor will be executed every time you construct the object!
What about providing an Initialize method that you can call whenever you need to reload the data??

If you want to load the data only once, then load it either into a static variable or a separate class that is referenced by the control.

If you really want to use the same single grid in your control over and over, you could create a single, static grid, and have your ClientLookUp constructor add it to the right place—Panel, or whatever—whenever a new one is created.
Before you go do this road however, ask yourself if this is really what you want to do. Having the same identical grid existing in many places may cause you problems down the road. If you want to support in-grid editing, you'll find that changing one value changes the identical value in all your other grids..
EDIT
I tried getting the below code to work, but I'm not sure this approach will be possible. It seems as though the minute you try to attach the same UI element into more than one place, it gets moved out of the last place you put it; it doesn't look like you can have the same grid being in more than one place at once. This makes sense when you think about it.
Here's the code I tried. Maybe it will be of some use to you.
public UserControl1()
{
InitializeComponent();
this.Controls.Add(myStaticGridView);
myStaticGridView.Dock = DockStyle.Fill;
}
static DataGridView _staticGrid;
public DataGridView myStaticGridView
{
get
{
if (_staticGrid != null)
return _staticGrid;
_staticGrid = new DataGridView();
_staticGrid.Columns.Add("A", "A");
_staticGrid.Columns.Add("B", "B");
_staticGrid.Columns.Add("C", "C");
_staticGrid.Columns[0].DataPropertyName = "A";
_staticGrid.Columns[1].DataPropertyName = "B";
_staticGrid.Columns[2].DataPropertyName = "C";
_staticGrid.DataSource = new[] {
new { A = "someA", B = "someB", C = "someC"},
new { A = "someA", B = "someB", C = "someC"},
new { A = "someA", B = "someB", C = "someC"},
new { A = "someA", B = "someB", C = "someC"},
};
return _staticGrid;
}
}
And then loading the control like this:
private void button1_Click(object sender, EventArgs e)
{
flowLayoutPanel1.Controls.Add(new UserControl1());
}

Related

How to modify control properties through variable reference

I've been working on making a project of mine more modular. Something I've wanted to do is have multiple buttons use the same function when they perform a similar action, but with different values. I've been stuck on trying to apply this to the following situation:
"When this button is clicked, have the user select an image, and then have a PictureBox display the selected image". Each button has its own PictureBox. All Controls have been created before runtime.
Hope that makes sense!
My last attempt can be seen in the code below- I have tried assigning the Controls(Button and PictureBox) to variables to be stored together in a class. There's 6 of these classes all included within a single List.
I've also tried to store only the Control Names and then using this.Controls.Find to retrieve the Controls.
I've tried quite a few smaller changes such as passing by reference, making the List static, and things such as that would (somehow)magically do the trick- I've gotten desperate.
public class score_control
{
public Button score_button;
public PictureBox score_picture;
public int picture_index;
}
public List<string> score_boxes_names = new List<string>();
public List<score_control> score_boxes = new List<score_control>();
public void add_score_control(Button button, PictureBox pictureBox)
{
score_control new_score = new score_control();
new_score.score_button = button;
new_score.score_picture = pictureBox;
new_score.picture_index = score_boxes.Count();
score_boxes.Add(new_score);
score_boxes_names.Add(button.Name);
}
public score_control find_score_control(string name)
{
int index = score_boxes_names.IndexOf(name);
return score_boxes[index];
}
public frm_settings()
{
InitializeComponent();
add_score_control(btn_score1_image1, pic_score1_image1);
add_score_control(btn_score1_image2, pic_score1_image2);
add_score_control(btn_score1_image3, pic_score1_image3);
add_score_control(btn_score2_image1, pic_score2_image1);
add_score_control(btn_score2_image2, pic_score2_image2);
add_score_control(btn_score2_image3, pic_score2_image3);
}
private void score_button_Click(object sender, EventArgs e)
{
Button image_button = (Button)sender;
if (ofd_png.ShowDialog() == DialogResult.OK)
{
score_control clicked_control = find_score_control(image_button.Name);
score_image[clicked_control.picture_index] = ofd_png.FileName;
clicked_control.score_picture.Image = Image.FromFile(ofd_png.FileName);
}
}
The problem seems centered around this line:
clicked_control.score_picture.Image = Image.FromFile(ofd_png.FileName);
The program throws a NullReferenceException , but clickedcontrol is being recognized in the Local Watch, as well as score_image being noted to be a PictureBox(as it should be).
When I instead held the Control Names in the class, I had broke this line down into multiple lines, but the following line produced a NullReferenceException:
Control[] find_control = this.Controls.Find(clicked_control.score_picture, true);
In this case, clicked_control.score_picture would be a string containing the PictureBox Name. Again, the Local Watch showed that it clicked_control was not null, and neither was score_picture.
Any help figuring out how to properly store a Control within a variable to later be used to modify that Control's properties would be greatly appreciated.
dontpanic was able to help me out with this one. The issue was actually outside of this code - it had to do with the line score_image[clicked_control.picture_index] = ofd_png.FileName;. The way score_image was initialized as an array was incorrect. Fixing that made everything work fine.

How do I clear a user control from a winform?

This is probably a basic question, but I can't find answers because the terms are generic.
I am building a WinForm aplication. Its purpose is to set up memory in a certain chip. I think the best way to organize the application is to have a user control for each chip type, derived from a generic parent class. Think of the children as "iphone," "android" and "blackberry," derived from a parent class "phone".
VS2017 Designer has a Panel where I want the control to be. On startup, I generate an object of the base class and add it to the panel. When I press a button, the old object is deleted and replaced with a new one. Each class has just one control, a label with distinctive text.
The problem is, after I press the button, I see both texts. The panel's Controls collection has just one element, but I see the text from both objects. I have tried Refresh, Update and Invalidate withe the same results.
What do I have to do to make the old text "go away" so the only thing I see is the latest object?
private ChipMemBase ChipMemControl = new ChipMemBase();
public Form1()
{
InitializeComponent();
//tbFeedback.Text = string.Format(fmtString, 0, 1, 2, 3, 4, 5);
cbChipName.SelectedIndex = 0;
tbVersion.Text = Version;
OriginalWindowColor = tbFeedback.BackColor;
ShowChipMemControl();
PrintToFeedback(Version);
}
private void ShowChipMemControl()
{
var ctl = pnlChipMem.GetChildAtPoint(new Point(5,5));
if (null != ctl)
{
if (ctl != ChipMemControl)
{
pnlChipMem.Controls.Remove(ctl);
ctl.Dispose();
pnlChipMem.Update();
Refresh();
}
}
if (null != ChipMemControl)
{
pnlChipMem.Controls.Add(ChipMemControl);
}
}
private void btnMakeChipMemory_Click(object sender, EventArgs e)
{
ChipMemControl = new ChipMemGen2();
ShowChipMemControl();
}
Screenshots before and after clicking Create
Your ShowChipMemControl gets the control at point 5,5 and checks if it's a ChipMemControl then removes it.
I'm guessing that the reason it's not getting removed is that the control at point 5,5 is not a ChipMemControl.
You can use:
pnlChipMem.Controls.Clear()
to remove all the controls
Or:
ChipMemControl cmc = pnlChipMem.Controls.OfType<ChipMemBase>().FirstOrDefault();
if (cmc != null)
{
pnlChipMem.Controls.Remove(cmc);
cmc.Dispose();
}
To only remove the first instance of ChipMemBase on your pnlChipMem panel.
Got it. The problem was from inheritance, not window behavior. Control lblDefault in the base class, carrying the inconvenient text, was still present in the child class. I had to make it Public in the base class and remove it in the child class constructor:
InitializeComponent();
Controls.Remove(lblDefault);
lblDefault.Dispose();
lblDefault = null;
The clue was this article and project:
dynamically-and-remove-a-user-control

Can I give controls an index property in C# like in VB?

I've found similar answers to my question before, but not quite to what I'm trying to do...
In Visual Basic (last I used it, in 06/07) there was an "Index" property you could assign to multiple controls with the same name. I used this primarily to loop through controls, i.e.:
For i = 1 to 500
picSeat(i).Print "Hello"
Next i
Is there a way to do this in C#? I know there is a .IndexOf(), but would that really help for what I'm doing? I want to have multiple controls with the same name, just different index.
This is a Windows Form Application, and I'm using Visual Studio 2012. I am talking about controls, not arrays/lists; this was possible in VB and I was wondering if it was possible at all in C#. So I want to have, say, 30 seats in a theatre. I want to have each seat represented by a picturebox named "picSeat". VB would let me name several objects the exact same, and would assign a value to a control property "Index". That way, I could use the above loop to print "Hello" in every picture box with only 3 lines of code.
No, this feature does not exist in C#, and was never implemented in the transition from classic VB to VB.Net.
What I normally do instead is put each of the controls in question in a common parent container. The Form itself can work, but if you need to distinguish these from others of the same type a GroupBox or Panel control will work, too. Then, you access the controls like this:
foreach (var picBox in parentControl.Controls.OfType<PictureBox>())
{
// do something with each picturebox
}
If you want to use a specific control, just write by name:
pictureBox6.SomeProperty = someValue;
If you need to change a specific control determined at run-time, normally this is in response to a user event:
void PictureBox_Click(object sender, EventArgs e)
{
var picBox = sender As PictureBox;
if (picBox == null) return;
//picBox is now whichever box was clicked
// (assuming you set all your pictureboxes to use this handler)
}
If you really really want the Control Arrays feature, you can do it by adding code to create the array to your form's Load event:
PictureBox[] pictureBoxes = Me.Controls.OfType<PictureBox>().ToArray();
Are we talking WinForms here? I'm not sure, but I don't think you can have multiple controls in winforms with same name. But I vaguely recall doing something similar and the solution was to name them Button_1, Button_2 etc. Then you can iterate through all controls and get your own index.
Beware though that if you want to instanciate a separate control for each seat in a theatre, you might run into some serious performance issues :) I've done something similar to that as well and ended up drawing the whole thing on a canvas and using mouse coordinates to handle the events correctly.
You may want to check out the Uid property of controls.
(http://msdn.microsoft.com/en-us/library/system.windows.uielement.uid(v=vs.110).aspx)
You can access Control through Uid property with the following
private static UIElement FindUid(this DependencyObject parent, string uid)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count == 0) return null;
for (int i = 0; i < count; i++)
{
var el = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (el == null) continue;
if (el.Uid == uid) return el;
el = el.FindUid(uid);
if (el != null) return el;
}
return null;
}
And simply use
var control = FindUid("someUid");
I copied code from this post
If you create an indexed dictionary of your user control, it will behave pretty much the same as in VB6, though you'll not see it on the VS C# GUI. You'll have to get around the placement issues manually. Still - and most importantly -, you'll be able to refer to any instance by the index.
The following example is for 3 pieces for clarity, but of course you could automate every step of the process with appropriate loops.
public partial class Form1 : Form
{
...
Dictionary<int, UserControl1> NameOfUserControlInstance = new Dictionary<int, UserControl1>()
{
{ 1, new UserControl1 {}},
{ 2, new UserControl1 {}},
{ 3, new UserControl1 {}}
};
private void Form1_Load(object sender, EventArgs e)
{
NameOfUserControlInstance[1].Location = new System.Drawing.Point(0, 0);
NameOfUserControlInstance[2].Location = new System.Drawing.Point(200, 0);
NameOfUserControlInstance[3].Location = new System.Drawing.Point(400, 0);
Controls.Add(NameOfUserControlInstance[1]);
Controls.Add(NameOfUserControlInstance[2]);
Controls.Add(NameOfUserControlInstance[3]);
}
...
}
I like using Tags to apply any type of meta data about the controls
for (int i = 0; i< 10; ++i)
{
Button button = new Button();
button.Tag = i;
}

C# Dynamic form (reflection) - linking controls

Sorry for the poor quality of the title. I couldn't think of a better way to phrase this.
For a project I'm currently working on with a few friends, I got myself in the situation where I have created a dynamic form (with reflection) which I now want to validate.
Example (ignore the black box, it contains old form elements which are now irrelevant and i didn't want to confuse you guys):
As you may have guessed already, it is an application for creating a mysql database.
Which is where I get to my problem(s). I want to disable checkboxes if others are checked.
For example: If I check "PrimaryKey" I want to disable the checkbox "Null".
Changing from unsigned to signed changes the numericupdown minimum and maximum etc.
But with reflection and all, I find it difficult to know exactly which checkbox to disable.
I was hoping you guys would have some suggestions.
I have been thinking about this for a while and a few thoughts have come to mind. Maybe these are better solutions than the current one.
Thought 1: I create UserControls for every datatype. Pro's: no problems with reflection and easy identifying of every control in the UserControl for validation. Con's: Copy-Pasting, Lots of UserControls, with a lot of the same controls.
Thought 2: Doing something with the description tags for every property of the classes. Creating rules in the description that allow me to link the checkboxes together. Here I'll only have to copy the rules to every class property and then it should be ok.
I had been thinking of other solutions but I failed to remember them.
I hope you guys can give me a few good pointers/suggestions.
[Edit]
Maybe my code can explain a bit more.
My code:
PropertyInfo[] properties = DataTypes.DataTypes.GetTypeFromString(modelElement.DataType.ToString()).GetType().GetProperties();
foreach (PropertyInfo prop in properties)
{
if (prop.Name != "Label" && prop.Name != "Project" && prop.Name != "Panel")
{
var value = prop.GetValue(modelElement.DataType, null);
if (value != null)
{
tableLayoutPanel1.Controls.Add(new Label { Text = prop.Name, Anchor = AnchorStyles.Left, AutoSize = true });
switch (value.GetType().ToString())
{
case "System.Int32":
NumericUpDown numericUpDown = new NumericUpDown();
numericUpDown.Text = value.ToString();
numericUpDown.Dock = DockStyle.None;
tableLayoutPanel1.Controls.Add(numericUpDown);
break;
case "System.Boolean":
CheckBox checkBox = new CheckBox();
checkBox.Dock = DockStyle.None;
// checkbox will become huge if not for these changes
checkBox.AutoSize = false;
checkBox.Size = new Size(16, 16);
if (value.Equals(true))
{
checkBox.CheckState = CheckState.Checked;
}
tableLayoutPanel1.Controls.Add(checkBox);
break;
default:
MessageBox.Show(#"The following type has not been implemented yet: " + value.GetType());
break;
}
}
}
}
Here is a mockup from my comments:
// The ViewModel is responsible for handling the actual visual layout of the form.
public class ViewModel {
// Fire this when your ViewModel changes
public event EventHandler WindowUpdated;
public Boolean IsIsNullCheckBoxVisible { get; private set; }
// This method would contain the actual logic for handling window changes.
public void CalculateFormLayout() {
Boolean someLogic = true;
// If the logic is true, set the isNullCheckbox to true
if (someLogic) {
IsIsNullCheckBoxVisible = true;
}
// Inform the UI to update
UpdateVisual();
}
// This fires the 'WindowUpdated' event.
public void UpdateVisual() {
if (WindowUpdated != null) {
WindowUpdated(this, new EventArgs());
}
}
}
public class TheUI : Form {
// Attach to the viewModel;
ViewModel myViewModel = new ViewModel();
CheckBox isNullCheckBox = new CheckBox();
public TheUI() {
this.myViewModel.WindowUpdated += myViewModel_WindowUpdated;
}
void myViewModel_WindowUpdated(object sender, EventArgs e) {
// Update the view here.
// Notie that all we do in the UI is to update the visual based on the
// results from the ViewModel;
this.isNullCheckBox.Visible = myViewModel.IsIsNullCheckBoxVisible;
}
}
The basic idea here is that you ensure that the UI does as little as possible. It's role should just be to update. Update what? That's for the ViewModel class to decide. We perform all of the updating logic in the ViewModel class, and then when the updating computations are done, we call the UpdateVisual() event, which tells the UI that it needs to represent itself. When the WindowUpdated Event occurs, the UI just responds by displaying the configuration set up by the ViewModel.
This may seem like a lot of work to set up initially, but once in place it will save you tons and tons of time down the road. Let me know if you have any questions.
Try relating the event of one checkbox to disable the other; something like this:
private void primaryKeyBox_AfterCheck(object sender, EventArgs e)
{
nullBox.Enabled = false;
}
This is a very simple example and would have to be changed a bit, but for what I think you're asking it should work. You would also have to add to an event for the boxes being unchecked. You would also need logic to only get data from certain checkboxes based on the ones that are and are not checked.
For all the other things, such as changing the numbers based on the dropdown, change them based on events as well.
For WinForms I would use data binding.
Create an object and implement INotifyPropertyChanged and work with that object.
Then, If you have an object instance aObj:
To bind the last name property to a textbox on the form do this:
Private WithEvents txtLastNameBinding As Binding
txtLastNameBinding = New Binding("Text", aObj, "LastName", True, DataSourceUpdateMode.OnValidation, "")
txtLastName.DataBindings.Add(txtLastNameBinding)
Take a look here for more info.
INotifyPropertyChanged

Add multiple controls to panel in webforms

I would like to be able to add multiple label controls to a panel and display them on an onlick event. The code I have does what I want it to the first time, then the label is simple replaced with a new label on the second onlick event and so on.
Here is what I have so far:
private void createTaskLabel(Guid GoalID, string Goal)
{
Label taskLabel = new Label();
taskLabel.ID = GoalID.ToString();
taskLabel.Text = Goal.ToString();
TaskPanel.Controls.Add(taskLabel);
}
So, for instance, this creates a new label with a uniqueID (handled elsewhere) and places it within the panel and displays it. When the onlick fires again, the same label is simply replaced instead of a new one appearing below it.
Dynamically created controls are not persisted after a postback. You need to keep track of how many controls you have generated and regenerate ALL of them each time for this to work how you want. Basic implementation:
List<string> LabeIDList = new List<string>();
override SaveViewState(..)
{
if (LabelIDList.Count>0) {
ViewState["LabelDIList"]=LabelIDList;
}
base.SaveViewState(..)
}
override LoadViewState()
{
base.LoadViewState();
if (ViewState["LabelIDList"]!=null) {
LabelIDList = (List<string>)ViewState["LabelIDList"];
}
}
override void OnLoad(..)
{
foreach (string id in LabelIDList)
{
// Make a function essentially like your code in createTaskLabel,
// you can use it there too
RecreateControl(id);
}
}
private void createTaskLabel(Guid GoalID, string Goal)
{
...
// save the ID you just created
LabelIDList.Add(taskLabel.ID);
}
I just realized that these are Labels you're creating - so actually you should probably be keeping track of the Text instead of the ID. If the actual ID is important for some reason then keep track of them both, use a List<Tuple<string,string>> for that. More typical situation is creating input controls, though, in which case ASP.NET will persist the data that a user entered as long as you re-create them on or before OnLoad (assuming ViewState enabled).
Dim lbl As New Label
Dim lbl1 As New Label
Dim txt As New TextBox
Dim txt1 As New TextBox
lbl.Text = "Name"
lbl1.Text = "class"
Me.Controls.Add(lbl)
Me.Controls.Add(txt)
Me.Controls.Add(lbl1)
Me.Controls.Add(txt1)
Have you look at Repeater control? It might make it a bit easier to implement I think.
At least you don't need to worry about the label control creations yourself.

Categories