C# dynamic update of control created in a dll - c#

I created a Control and compiled it into a DLL:
namespace TSControlLibrary
{
public partial class BaseMaskedTextBox : MaskedTextBox
{
public BaseMaskedTextBox()
{
InitializeComponent();
this.BackColor = Color.Pink;
}
}
}
Then I created another Test project and want to use this new MaskedTextBox on it...
Which pretty much works,
1 - I added the reference to the DLL in Test. And kept the default properties. Copy Local = True, Specific Version = False
2 - I added the DLL to the toolbox, which shows the BaseMaskedTextBox.
Now I can add a new masked thingy onto my winform. :-)
BUT!!! But when I want to change the original DLL from Color.Pink to Color.Blue it will allow me to add a new MaskedTextBox on the form in Blue. But the Pink is still Pink.
What I would like to do is change the DLL color, recompile that and the new Test project will have the new color. How can I do this?
VS2012, winforms, heh.

hmm... well, it looks like I figured it out. :-)
I found that the DLL color set works, it updates the color from the default to Green or whatever, but then the Test.Designer.cs runs after that and just resets it to whatever it was when I added it hours ago.
So after a while on my Test form I have 30 different boxes all different colors. Because the designer.cs keeps track of where they go and some other things - like colors and font size and things.
So, I found that LocationChanged event will fire after the designer, but before it shows on the screen. So that was the ticket.
public BaseMaskedTextBoxDate2()
{
this.LocationChanged += new EventHandler(Setup);
}
//*************************************************************************************************
private void Setup(object sender, EventArgs e)
{
MaskedTextBox maskedBox = (MaskedTextBox)sender;
maskedBox.BackColor = Color.Gray; // For testing and also to make sure all fields are handled.
maskedBox.Font = new Font("Microsoft Sans Serif", 10.0f);
maskedBox.ValidatingType = typeof(System.DateTime);
maskedBox.BeepOnError = false;
maskedBox.TypeValidationCompleted += new TypeValidationEventHandler(maskedTextBoxDate_TypeValidationCompleted);
...
}
Now, if I have the DLL version ( above ) open in an instance of VS and make a change, say to the font size, going from 10 to 20, on the other instance of VS that is Test it will update on the Test Design View instantly - and with 20 font it will also be pretty nasty looking... And when compiled it will also be correct - as shown in Design.
Oddly, the only thing that I can't change is the Mask. But I doubt I will ever change that from 00/00/0000 - at least for this project anyway.

Related

MetroFramework for windows forms project and tab control rendering issue

I'm not sure where to begin here in order to troubleshoot and work on the 3rd party code to try and fix this so I thought I would ask here and show a screen of the issue. I am using a third party windows form "metro controls" package from https://thielj.github.io/MetroFramework/ and it looks a bit dated and no longer maintained and the only real issue I am having is that the tab control specifically seems to have extra rendering "junk" that goes away when you click it. But I don't know why it's drawn in the first place or how to go about removing / fixing it and updating the package on github. The UI package is useful it makes a plain windows form project look much nicer and have a far better UI so that's the reason I picked it up.
Is anyone familiar with this library and having similar issues or know the best way to approach fixing it myself? Keep in mind I am not familiar with the drawing/gui portion of windows forms at all and usually reference online material when it comes to drawing or UI type work.
Turned out I found this library instead (slightly newer / more updates) https://www.nuget.org/packages/MetroModernUI/ but still has some similar issues I am having however I modified the code slightly to help accommodate one of the issues I am having, the OnDrawItem event lifecycle seems to be gone for setting OwnerDrawFixed property on a tab control.
Here is the code I modified in MetroTabControl.cs and I am just using a local version/build to support any customizations and enhancements I have to do since there is a few other problems I have as well.
private void DrawTab(int index, Graphics graphics)
{
Color foreColor;
Color backColor = BackColor;
if (!useCustomBackColor)
{
backColor = MetroPaint.BackColor.Form(Theme);
}
TabPage tabPage = TabPages[index];
Rectangle tabRect = GetTabRect(index);
if (!Enabled || tabDisable.Contains(tabPage.Name))
{
foreColor = MetroPaint.ForeColor.Label.Disabled(Theme);
}
else
{
if (useCustomForeColor)
{
foreColor = DefaultForeColor;
}
else
{
foreColor = !useStyleColors ? MetroPaint.ForeColor.TabControl.Normal(Theme) : MetroPaint.GetStyleColor(Style);
}
}
if (index == 0)
{
tabRect.X = DisplayRectangle.X;
}
Rectangle bgRect = tabRect;
tabRect.Width += 20;
using (Brush bgBrush = new SolidBrush(backColor))
{
graphics.FillRectangle(bgBrush, bgRect);
}
TextRenderer.DrawText(graphics, tabPage.Text, MetroFonts.TabControl(metroLabelSize, metroLabelWeight),
tabRect, foreColor, backColor, MetroPaint.GetTextFormatFlags(TextAlign));
//HACK not properly handling the owner draw fixed event/override life cycle so we can fire it ourselves and if something is listening
// it will execute
if (this.DrawMode == TabDrawMode.OwnerDrawFixed)
{
OnDrawItem(new DrawItemEventArgs(graphics, this.Font, tabRect, index, DrawItemState.None));
}
}
For the other UI issue I am tracking down where I can invalidate or fix the odd additional button / clickable disappearing areas in the tab control or panels for this UI enhancement framework.
What happens is that there should be some error related to the Size of the MetroTabControl, and you can fix that with the following code:
this.metroTabPage1.VerticalScrollbarBarColor = false;
this.metroTabPage1.VerticalScrollbarSize = 0;

How to subsequently add scaling behaviour in a WinForms application?

I am working on a WinForms application using Visual Studio 2008 (C#). The user interface of the concerning form consists of several SplitContainers.
When I tested the application after setting the Windows font size to 125%, the form didn't look any more as it should. There obviously was a problem with scaling. Therefore I've searched for a solution and found one here. The two following lines did the job for me:
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
As explained in the other question (in one of the answers, respectively), they have to be included for every container in the designer file. It works and scaling performs correctly. But on the other hand, I have to manually edit the designer file and that's something one actually shouldn't do. The added lines get lost every time I use the layout designer.
EDIT: Just to clarify: These two properties are not shown in the designer gui.
Now finally, my question is: What can I do? How or where can I add the code for correct scaling without dirty manipulation of the designer file?
I have already tried to simply put these two lines for every container in the constructor after the InitializeComponent() method call, but at this position, they don't have the desired effect.
So, maybe you have an idea how it has to be done.
Thanks in advance,
Alex
I've finally found a way to solve the problem. It isn't really what I intended originally, but it has the same effect.
The concerning two properties don't show in the designer gui, so why not make them appear? Therefore I created a custom control and added some attributes to both scaling properties, so that they appear in the designer.
public class ScalableSplitContainer : SplitContainer
{
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Bindable(true)]
public new AutoScaleMode AutoScaleMode
{
get { return base.AutoScaleMode; }
set { base.AutoScaleMode = value; }
}
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Bindable(true)]
public new SizeF AutoScaleDimensions
{
get { return base.AutoScaleDimensions; }
set { base.AutoScaleDimensions = value; }
}
}
Using this specialized SplitContainer, scaling behaviour can easily be set in the gui and the code lines are included in the generated designer file.

Recreate a group of Controls

Let's say that I have a panel with like... 3 controls in it. I may end up adding more controls to it or changing the positioning within that panel. When the program starts, I will programmatically HIDE the control. Eventually, the user can click a button that will create a duplicate of the original panel to populate an area on the form. The button should have the option for another click eventually, meaning that multiple instances of these can come about to populate this area. Remember that these controls may have text labels within them that can be individually set or altered later on, programmatically. I am assuming that to do this program, I need to make a List of controls, maybe a List of panels? I'm not exactly sure how to do this considering the fact that I need multiple controls duplicated multiple times.
Is there a nice, simple way to do this? I really don't want to do the duplication with any kind of 3rd-party package.
You will have to do it in code and therefore it'll be as nice as you can code ;-)
Seriously the course most often taken is to
create a UserControl which is a class related to a form, with the layout you want..
..and add more and more instances of it..
..often to a FlowLayoutPanel, often with AutoScroll
This is pretty nice and simple imo.
Here is a short walk-though..:
first we start, as usual, by picking a nice name for the UserObject class, maybe 'DataPanel' or 'BookItem'..
Next we create it: Go to the project explorer and right-click, choosing Add-New UserControl and give it the class name you chose. I'll use 'BookItem'.
Now you can see the Designer showing you a small empty control.
Look closer: You can also see that in the project explorer ther is now not only the new 'BookItem.cs' file but also the complementary 'BookItem.Designer.cs' and even a 'BookItem.resx' file; so this works very much like creating a new Form..
Let's add a few controls from the toolbox, I chose to add a PictureBox, four Labels and a NumericUpDown.
Have a look at the BookItem.Designer.cs file: Here you can see the very things you see in a Form.Desginer.cs file: All settings and all declarations for all controls you add to the layout. Note especially the declarations (at the bottom of the file): Just like for a Form, all controls by default are declared as private!
We can now work on the layout and script the controls. We also can add functions and properties to the UC, just like a Form.
Please note: Anything you need to access from outside, read from your form or its methods must be public! So if you want to access the NUpDown, let call it 'nud_quantity' you have a choice
You can change its declaration in the BookItem.Designer.cs from private to public or in the Designer by changing the Modifiers property
Or you can write a public function in the UC to get/set its value
Chosing between those two ways is a matter of taste; if other developers will work with the UC class, it will probably be better to put close control over what you expose by writing access methods.
After you have compiled the project you can see the new UC in the Toolbox.
You can now either add it from the Toolbox or
you can add it in code like any control you create dynamically.
Let's look at an example:
Imagine a simple order system in a bookstore: The customer has done a search on the books in our store and is presented with a list of books in a DataGridView 'dgv_bookList', readonly, multiselect. To the right there is a FlowLayoutPanel 'flp_cart' represeting a shopping cart. And we have a command button 'cb_addItems' to add selected books to the cart.
The Button might be scripted like this:
private void cb_addItems_Click(object sender, EventArgs e)
{
if (dgv_bookList.SelectedRows.Count <= 0) return;
foreach (DataGridViewRow row in dgv_bookList.SelectedRows)
{
BookItem book = new BookItem (row);
book.label1.Text = "#00" + book.label1.Text;
book.Name = book.label1.Text;
flp_cart.Controls.Add(book);
}
}
This will add one BookItem for each selected row in the DGV.
A few things to note on the above code:
I pass a DataGridViewRow into the constructor of the UC so it can directly set its labels! This means that, in addition to the parameterless contructor the desginer has built for us, we need to write a second contructor, maybe like this:
public bookItem()
{
InitializeComponent();
}
public bookItem(DataGridViewRow bookData)
{
InitializeComponent();
label1.Text = bookData.Cells[0].FormattedValue.ToString();
label2.Text = bookData.Cells[1].FormattedValue.ToString();
label3.Text = bookData.Cells[2].FormattedValue.ToString();
label4.Text = bookData.Cells[3].FormattedValue.ToString();
}
Instead you could write a public setData(DataGridViewRow bookData) function.
Also note how stupid my labels are named! You can do better than that, I hope!
Also note how I access 'label1' and modify its Text from a Button in the Form; to do that I had to change its declaration in the Desginer.cs file:
private System.Windows.Forms.PictureBox pb_cover;
public System.Windows.Forms.Label label1; // <<----expose this label !
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.NumericUpDown numericUpDown1;
Often preferrable: An access function, maybe like this:
public int quantity() { return (int) numericUpDown1.Value; }
Or, of course a Property:
public int quantity { get { return (int)numericUpDown1.Value; } }
Also note, that I set the Name of the BookData item to some variant of the 1st data item, my book id. This might as well, or better, happen in the constructor; and there should be a check to prevent adding the same item twice..
All in all one can say, that using UserControls is very much like working with Forms, including all the usual ways or tricks for inter-form communication: keep references, expose members, create properties and functions..
One final Note: Like with forms or subclassed controls there is one catch: By placing them in the designer, you assign the designer the responsiblity to display your UC during design time.
This is normally just fine; however it is also possible to introduce subtle mistakes which make it impossible for the designer to display the control. You need to correct these problems before the designer will be able to show a control or any form that contains it. Let have a look at a simple example of such a problem:
Let's script the Paint event of the PictureBox 'pb_cover' in the UC:
public Brush myBrush = null;
private void pb_cover_Paint(object sender, PaintEventArgs e)
{
if (pb_cover.Image == null)
{
Size s = pb_cover.ClientSize;
e.Graphics.FillRectangle(myBrush, 0, 0, s.Width, s.Height);
e.Graphics.DrawLine(Pens.Red, 0, 0, s.Width, s.Height);
e.Graphics.DrawLine(Pens.Red, s.Height, 0, 0, s.Width);
}
}
And let's modify the code in the Add button:
BookItem book = new BookItem (row);
book.label1.Text = "#00" + book.label1.Text;
book.myBrush = Brushes.OliveDrab;
flp_cart.Controls.Add(book);
Now, if you run the program all will be fine. Even if you try to look at the UC in the designer there may or may not be problems. But once you try to open a Form on which the UC was placed, the Desginer will crash and tell you that it can't work, since the Brush is null. Here the remedy is simple: add a default value to the Brush declaration and all is well. Other situations may need a little more thinking..
I don't even run into the problem btw, since I have not placed an instance of BookItem on the Form; they are only created in the Add Button..
I hope that gets you started!

Can I stop Visual Studio auto-changing the form designer code?

I'm using visual studio and have some numericUpDown controls that I have set values for (min, max, value and increment). The Code Designer keeps turning my simple decimal values into int[]. I know that it says "do not modify the contents of this method with the code editor" but I find it really useful to adjust properties in here and I thought I was just "making things neat" when I changed:
this.numericUpDown1.Increment = new decimal(new int[] {
2,
0,
0,
0});
to
this.numericUpDown1.Increment = new decimal(2);
but alas, it changes it back if I touch the control on the visual form designer. I thought maybe there was a flag that would leave it as is until I want it to be updated. It is more for readability and navigation but even if it would just leave it on one line I'd be happier.
I've found this (How can I tell Visual Studio to not populate a field in the designer code?) where someone was trying to leave it but I'm not sure if it is applicable in this instance.
Feel free to tell me that I should just get over it and leave it alone!
Just realised (thanks #Jonathan) that I could just create a separate section in the designer ABOVE that place ... this means I can have all my default values in a separate section as long as I don't want to changed the defaults on the form designer. This is the method above InitializeComponent()
private void IntializeOtherComponents()
{
this.numericUpDown1.Value = new decimal(17);
this.numericUpDown1.Maximum = new decimal(7777);
this.numericUpDown1.Minimum = new decimal(5);
}
You could make your own NumericUpDown user control. Just inherit from the common NumeriUpDown and set the dessired properties in the constructor.
public partial class MyUpDownCtrl : NumericUpDown
{
public MyUpDownCtrl()
{
InitializeComponent();
this.Increment = new decimal(2);
}
}
After generating your solution you will have your new custom UpDownControl in your Tool Box ready to use just dragging it like the common numericUpDown.
I hope it help you, because I don't know any way to make Visual Studio stop auto-changing the form designer code. Maybe it can't be done at all.

Why does my listview keep drawing in LargeIcon View?

I have a inherited Listview which standard has to be in Tile Mode. When using this control, the DrawItem gives e.bounds which are clearly bounds of largeIcon view ?? When debugging to check the view it is actually set to, it says it's in Tile view ?? Yet e.DrawText draws LargeIcon view ??
......... Edit: .................
This seems only to happen when the control is placed upon another usercontrol?
......... Edit 2: .................
It gets stranger ... When i add buttons next to the list to change the view at runtime, "Tile" is the same as "LargeIcon", and "List" view is the same as "SmallIcons" ??? I've also completely removed the ownerdraw ...
.......... Edit 3: .................
MSDN Documentation:
Tile view
Each item appears as a full-sized icon
with the item label and subitem
information to the right of it. The
subitem information that appears is
specified by the application. This
view is available only on Windows XP
and the Windows Server 2003 family.
On earlier operating systems, this value is ignored and the ListView
control displays in the LargeIcon
view.
Well I am on XP ?!?
...... Edit 4 .....................
Holy mother of strangeness ...
We are now at the point we've completely stripped down EVERYTING ... We have a standard listview on a form, manually filled with 3 values. No Ownerdraw. It is set to Tile.
When we start this form, the list is drawn as LARGEICON.
Now, we start another blank solution, copy this exact same form to the new project, start debug and low and behold .. it is drawn in TILE view ????
... help ...
public class InheritedListView : ListView
{
//Hiding members ... mwuahahahahaha //yeah i was still laughing then
[BrowsableAttribute(false)]
public new View View
{
get { return base.View; }
}
public InheritedListView()
{
base.View = View.Tile;
this.OwnerDraw = true;
base.DrawItem += new DrawListViewItemEventHandler(DualLineGrid_DrawItem);
}
void DualLineGrid_DrawItem(object sender, DrawListViewItemEventArgs e)
{
View v = this.View;
//**when debugging, v is Tile, however e.DrawText() draws in LargeIcon mode,
// e.Bounds also reflects LargeIcon mode ???? **
}
................................
This code behaves differently at different solutions:
private void InitializeComponent()
{
System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("fhsdhdsfhsdfhs");
System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem("fdshdsfhdsfhsd");
System.Windows.Forms.ListViewItem listViewItem3 = new System.Windows.Forms.ListViewItem("hdshsdfhsdfhsdfsdfsdf");
this.listView1 = new System.Windows.Forms.ListView();
this.SuspendLayout();
//
// listView1
//
this.listView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
listViewItem1,
listViewItem2,
listViewItem3});
this.listView1.Location = new System.Drawing.Point(36, 12);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(487, 242);
this.listView1.TabIndex = 2;
this.listView1.TileSize = new System.Drawing.Size(480, 50);
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Tile;
//
// TestControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(595, 712);
this.Controls.Add(this.listView1);
this.Name = "TestControl";
this.Text = "TestControl";
this.ResumeLayout(false);
}
#endregion
OK, we found it. The magic spell is:
Application.EnableVisualStyles();
We skipped this line of code to test our form.
If you don't call this method before you create your form with your listview, TILE view gets drawn as LARGEICON.
Seems totally logical ... :-(
http://blogs.msdn.com/rprabhu/archive/2003/09/28/56540.aspx
Q What does Application.EnableVisualStyles actually do?
Windows XP ships with two versions of the Common Controls Library (comctl32.dll) - versions 5.8 and 6.0. v5.8 renders controls in the "Classic" style that you get on Windows NT/2000 and Windows 9x. v6.0 renders controls using the XP Visual Styles look and feel. Since most Windows Forms controls are based on comctl32, how they are rendered depends on which version of comctl32 is used to do the rendering. By default, v5.8 is used to render the client area of the app and v6.0 is used to render the non-client area. That is why you see the title bar and window borders automatically render "themed", while the controls (like Button, TextBox, ListView, ComboBox and so on) have the classic look by default.
In v1.0 of the Framework, the way to get visual styles in a Windows Forms app was to ship a manifest file with the app, that has information in it to indicate that v6.0 of comctl32 should be used for rendering. While this works fine, many developers felt it cumbersome to author, maintain and deploy manifest files. They felt the need to be able to do this programmatically. Now, the Platform SDK does provide API to do this. Basically, you need to create and activate an Activation Context that has pretty much the same DLL redirection information in it as the manifest file. The Activation Context API can be used to do this in a way suitable to your application.
If you take a look at these API, you will probably notice that they aren't very easy to use. While the advanced developers may like to tinker around with activation contexts, it is probably not something a developer who wants some "quick and dirty" code to get visual styles will do. So the Windows Forms team decided to wrap these API and expose a simple method that developers could call, that would isolate them from these complexities. So, essentially, when you call Application.EnableVisualStyles, we set up an activation context around the application's message loop, so that comctl32 function calls can be properly redirected to comctl32 v6.0. That way, you don't need to include a manifest with your app.

Categories

Resources