I want to bind a winform's form's Width property to Text on a label so label's text gets updated every mouse movement I made. Currently I only achieved updating when some element on a form is clicked but not continious updating(like if you change text in Resize handler). How to do this thing?
You can bind to the Width property by doing this:
label1.DataBindings.Add(new Binding("Text", this, "Width"));
The problem there is the form isn't notifying the framework that the property has changed. Your easiest best bet is likely to just do it the meat and potatoes way:
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
label1.Text = this.Width.ToString();
}
EDIT: Okay, if you really want to use data binding, here is a way that works (but is like reaching around your head to scratch your ear):
Add an object data source to your form and set the DataSource to type "System.Windows.Forms.Form".
Next, add some code:
public Form2()
{
InitializeComponent();
this.formBindingSource.DataSource = this;
Binding binding = new Binding("Text", this.formBindingSource, "Size", true);
binding.Format += new ConvertEventHandler(binding_Format);
label1.DataBindings.Add(binding);
}
void binding_Format(object sender, ConvertEventArgs e)
{
Size size = (Size)e.Value;
e.Value = size.Width.ToString();
}
So like I said, it's complete overkill, but it works.
The Resize event is the correct event to handle. I'm not sure what continuous updating you are looking for, but if the form changes size, Resize Event fires. I believe this also includes size changes for minimize/maximize/restore. This should cover all changes to the size of the form.
private void OnFormResize(object sender, EventArgs args)
{
Form frm = (Form) sender;
txtWidth.Text = frm.Size.Width.ToString();
}
You are right, binding to the Width property will not working correct because Form hasn't WidthChanged event.
You can bind to the Size property and use formating to format that
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var binding = new Binding("Text", this, "Size", false, DataSourceUpdateMode.OnPropertyChanged);
binding.Format += new ConvertEventHandler(binding_Format);
label1.DataBindings.Add(binding);
}
void binding_Format(object sender, ConvertEventArgs e)
{
if (e.Value is Size)
{
e.Value = ((Size)e.Value).Width.ToString();
}
}
}
Related
I have dynamic Content for a Label in WPF. How can I change the foreground color of a label when text of the label changed, I have the code for Winform but I need it for WPF..
Winform Code in Designer Side
this.lblSolar.Name= "lblSolar";
this.lblSolar.TextChanged + = new System.EventHandler(this.LblSolar_TextChanged);
Code of text_Changed function
private void LblSolar_TextChanged(object sender, EventArgs e)
{
var solarCurrent= Convert.ToDouble(_sValues[(int)ValueOfRTC.SupplyCurrent]);
if (supplyCurrent < 1)
{
lblSupply.ForeColor= Color.Yellow;
}
else
{
// Assigning other colors.....
}
}
But I couldn't found "TextChanged" Property in WPF XAML.. then what is the Solution?
Thanks in advance
Use TextBox instead of Label
vb.net
Private Sub TextBox1_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextBox1.TextChanged
TextBox1.Foreground = Brushes.Yellow
End Sub
C#
private void TextBox1_TextChanged(object sender, TextChangedEventArgs e) {
TextBox1.Foreground = Brushes.Yellow;
}
In WPF a Label is not only a control to present Text.
It is a ContentPresenter that can display all kinds of controls other than text as well. Thus it has no text changend event.
Generally speaking you should not use events in the same manner when using WPF as you did with winforms. You should really consider to start lerning MVVM pattern as it will make your life a lot easier in a long term (after a rough start).
However if you insist in usng events to do this you NEED to use a Textbox...BUT you can make the textbox look like a label like this:
this.lblSolar.Name= "lblSolar";
this.lblSolar.TextChanged + = new System.EventHandler(this.LblSolar_TextChanged);
this.lblSolar.BorderThickness = 0;
this.lblSolar.Background = System.Drawing.Color.Transparent;
this.lblSolar.IsReadOnly = True;
What Denis said is right. If you still want to achieve this with Label, you can simply derive your own label control that provides a ContentChanged event like this.
public class MyLabel : Label
{
static MyLabel()
{
ContentProperty.OverrideMetadata(typeof(MyLabel),
new FrameworkPropertyMetadata(
new PropertyChangedCallback(OnContentChanged)));
}
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyLabel lbl = d as MyLabel;
if (lbl.ContentChanged != null)
{
DependencyPropertyChangedEventArgs args = new DependencyPropertyChangedEventArgs( ContentProperty, e.OldValue, e.NewValue);
lbl.ContentChanged(lbl, args);
}
}
public event DependencyPropertyChangedEventHandler ContentChanged;
}
You can use this in XAML like this.
<local:MyLabel Content="Sample" ContentChanged="MyLabel_ContentChanged"/>
I have a button which I use all the time as a little pick button next to a combobox. When I click the button I open a larger full list. This side of things work well and I do not have a problem with this..
My problem lies when someone said to me can you change that ugly icon you picked to my nice icon.
I went crap, I have hundreds of these buttons on many forms. So I thought I will create a custom control called PickButton (which is a standard button and heap of default proeprties set) and drop these on the form everywhere instead. In the code of the PickButton custom control I set some properties and the image to the customers nice icon.
So I drop the PickButton from my toolbox onto the form, so far things are looking pretty good and I am feeling a bit clever. Now I think to myself I will change back to my nice icon not the crappy one the customer picked and change the code in the PickButton custom control. But I cannot get rid of that customers icon, because the code when the PickButton run happens before the code in the designer file which has the customers icon.
So my aim was to have a PickButton control and be able to change the icon and other properties in one place and all the properties would be set when an instance of the control is created and displayed on the form.
Was I not so clever and went about achieving the task the wrong way???
This is my PickButton custom control class
public class PickButton : Button
{
public PickButton()
{
InitialiseButton();
}
internal void InitialiseButton()
{
this.ImageAlign = ContentAlignment.MiddleCenter;
this.Image = WindowsFormsApplication1.Properties.Resources.Cancel.ToBitmap();
this.Size = new Size( 28, 28 );
this.Dock = DockStyle.Fill;
this.Margin = new Padding( 0, 2, 2, 0 );
this.Text = string.Empty;
}
}
Now I drop one onto my form and the code in the designer is as follows
//
// pickButton1
//
this.pickButton1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pickButton1.Image = ((System.Drawing.Image)(resources.GetObject("pickButton1.Image")));
this.pickButton1.Location = new System.Drawing.Point(0, 0);
this.pickButton1.Margin = new System.Windows.Forms.Padding(0, 2, 2, 0);
this.pickButton1.Name = "pickButton1";
this.pickButton1.Size = new System.Drawing.Size(284, 262);
this.pickButton1.TabIndex = 0;
this.pickButton1.Text = "pickButton1";
this.pickButton1.UseVisualStyleBackColor = true;
Now I want to change the image so I change my PickButton code to use a different icon
this.Image = WindowsFormsApplication1.Properties.Resources.Browse.ToBitmap();
Run the application andd the first icon is still the one being displayed because of this line of code in the designer file
this.pickButton1.Image = ((System.Drawing.Image)(resources.GetObject("pickButton1.Image")));
The concept of setting all the properties in one place was a good idea, it just wasn't implemented quite right. I would make this class inherit from UserControl instead of from Button. By making it a UserControl, you can use the designer to set all the properties you want, like the default Image for the button. Set that in the designer, then just drag and drop your UserControl from the toolbox onto your forms. If you are only using your "PickButton" control with comboboxes, I would put the combobox on the UserControl as well. If you ever want to change your button image in the future (or any other property for that matter), you will be able to change it in ctlPickButton and that will propogate the changes to all the instances used throughout your project(s).
ctlPickButton:
public partial class ctlPickButton : UserControl
{
public event EventHandler pickButtonClicked;
public ctlPickButton()
{
InitializeComponent();
}
//Allows buttons image to be set in code if necessary
public Image Image
{
get
{
return button1.Image;
}
set
{
if (Image != null)
{
button1.Image = value;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
if (pickButtonClicked != null)
{
pickButtonClicked(sender, e);
}
}
}
Demo Form:
public Form1()
{
InitializeComponent();
ctlPickButton1.pickButtonClicked += new EventHandler(ctlPickButton1_pickButtonClicked);
ctlPickButton2.pickButtonClicked += new EventHandler(ctlPickButton2_pickButtonClicked);
}
void ctlPickButton2_pickButtonClicked(object sender, EventArgs e)
{
if (comboBox2.SelectedItem != null)
{
MessageBox.Show(comboBox2.SelectedItem.ToString());
}
}
void ctlPickButton1_pickButtonClicked(object sender, EventArgs e)
{
if (comboBox1.SelectedItem != null)
{
MessageBox.Show(comboBox1.SelectedItem.ToString());
}
}
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Items.Add("French");
comboBox1.Items.Add("Spanish");
comboBox1.Items.Add("English");
comboBox1.Items.Add("German");
comboBox2.Items.Add("Pizza");
comboBox2.Items.Add("Hamburger");
comboBox2.Items.Add("Potato");
comboBox2.Items.Add("Chicken");
//Shows how the default image set in the designer can be overwritten for a
//specific instance using the "Image" property
ctlPickButton2.Image = Testbed.Properties.Resources.searchIcon2;
}
}
Image of ctlPickButton in designer
I think I've found a simple, clean solution:
In the CustomButton class (which inherits from System.Windows.Forms.Button), override the Refresh() method, and set the image of the button to the one you want to see:
public class CustomButton : Button
{
public override void Refresh()
{
Image = MyResources.HappyFace;
}
}
In the form that will hold an instance of your CustomButton, simply call customButton.Refresh() in the constructor, after InitializeComponent():
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
customButton.Refresh();
}
}
I've put a demo application up on Github.
I notice all the other controls I am using such as tabs, textboxes, rich textboxes with text already inside it and everything else resize automatically when I change the main Form font. But labels are an exception to this rule is there a property I can turn on or off in the Label that will make it resize font with the form? It is a pretty straightforward yes or no question I know how to work around it I just like how all my controls are automatically resizing without any unnecessary code.
I've a solution here:
If your controls have the same font with your Form's Font, you shouldn't change the particular Font of them (via the Properties window or via code), instead you just change your Form's Font. If that has already been done, you can find the code lines (normally in Form.Designer.cs) which specify Font for your controls (something like yourControl.Font = ...) and remove those lines.
If your controls need different Font from your Form's. You can register the FontChanged event handler of your Form and change its children controls' Fonts accordingly (just the font size):
private void Form1_FontChanged(object sender, EventArgs e){
UpdateChildrenFont(this);
}
private void UpdateChildrenFont(Control parent){
foreach(Control c in parent.Controls){
c.Font = new Font(c.Font.FontFamily, parent.Font.Size, c.Font.Style);
UpdateChildrenFont(c);
}
}
The recursive method UpdateChildrenFont works in most cases but if you have some TabControl on your form, that doesn't work, because TabControl has another kind of collection to hold its tab pages called TabPages... I've encountered such a case. Another solution is to create your own Controls and override the OnParentChanged to register the Parent.FontChanged event handler accordingly, like this:
public class YourControl : TheBaseControl {
protected override void OnParentChanged(EventArgs e){
if(Parent != null){
Parent.FontChanged -= ParentFontChanged;
Parent.FontChanged += ParentFontChanged;
}
}
private void ParentFontChanged(object sender, EventArgs e){
Font = new Font(Font.FontFamily, Parent.Font.Size, Font.Style);
}
}
You should apply that model on all your controls on your form. That's very clean although requires you to have custom classes for your controls.
The last solution I can think of is playing with ControllAdded event, this is applied only on your Containers such as Form, GroupBox, Panel... and every control which has some child control on your form. Here is the code:
public class YourContainerClass: YourContainerBaseClass {
protected override void OnControlAdded(ControlEventArgs e){
Binding bind = new Binding("Font", this, "Font");
bind.Format += (s, ev) =>
{
Font inFont = (Font)ev.Value;
Binding bin = (Binding)s;
ev.Value = new Font(bin.Control.Font.FontFamily, inFont.Size, bin.Control.Font.Style);
};
e.Control.DataBindings.Clear();
e.Control.DataBindings.Add(bind);
base.OnControlAdded(e);
}
}
Or simply if that's your Form:
public class Form1 : Form {
public Form1(){
ControlAdded += FormControlAdded;
InitializeComponent();
}
private void FormControlAdded(object sender, ControlEventArgs e){
Binding bind = new Binding("Font", this, "Font");
bind.Format += (s, ev) =>
{
Font inFont = (Font)ev.Value;
Binding bin = (Binding)s;
ev.Value = new Font(bin.Control.Font.FontFamily, inFont.Size, bin.Control.Font.Style);
};
e.Control.DataBindings.Clear();
e.Control.DataBindings.Add(bind);
}
}
I am adding controls after the shown event on Form. The controls are showing up one at a time despite the fact I called SuspendLayout(). How can I get the layout to suspend so the controls only display when they are all finished loading?
public partial class ControlCreateTest : Form
{
public ControlCreateTest()
{
InitializeComponent();
}
private void AsyncControlCreateTest_Shown(object sender, EventArgs e)
{
CreateControls();
}
private void CreateControls()
{
SuspendLayout();
int startPoint= 0;
for (int i = 0; i < 4; i++)
{
UserControl control = new UserControl() { Text = i.ToString(), Height = 100, Width = 100 };
control.Load += control_Load;
Controls.Add(control);
control.Top = startPoint;
startPoint += control.Height;
}
ResumeLayout();
Text = "Loading complete";
}
void control_Load(object sender, EventArgs e)
{
Thread.Sleep(500);
RichTextBox newRichTextBox = new RichTextBox() { Height = 100, Width = 100 };
UserControl control = sender as UserControl;
control.Controls.Add(newRichTextBox);
newRichTextBox.Text = "loaded";
}
}
UPDATE
It seems that once these forms begin loading...the visibility and suspend calls are thrown out the window immediately. That is quite troublesome when the Load events are long running.
Getting a little hacked at the obscurity of Winforms dev. Anyway...I set the width and height of the form to 1 pixel in the constructor. When show is called I hide the window and put the window back to normal size. It's hard to notice the tiny window before it's hidden.
This lets my routines fire up and loading form display without all the headache.
UPDATE
When using ShowDialogue(), this dumb little trick only works if you Set Visible = true before Form_Shown returns control to the caller. I found that if you set Visible = true in Form.Shown the Closing event will be triggered...man I flipping love WINFORMS....
Try using AddRange to add all your controls at once:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.controlcollection.addrange.aspx
I am new to using Event Handling in C# .NET, and I am trying to understand the behavior behind some simple code that I am experimenting with. I am working with a more complicated example, but I am hoping I will get a more focused answer if I simplify the example.
I have the following code which defines a main window with a ListBox that is initialized with values, and a panel in the window. I am working with dragging the ListBox Items and dropping them in the panel. To signify that the panel is reading the DragDrop event, I am simply just changing the background color.
My problem is, it is not changing the background color when I drop the values, hence, the DragDrop is not working. I know this is a bit exaggerated, but I am trying to understand why its not working.
Here is the following code that I am using.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//Allow Panel to accept dropped values
this.panel1.AllowDrop = true;
//Initialize ListBox with sample values
listBox1.Items.Add("First Name");
listBox1.Items.Add("Last Name");
listBox1.Items.Add("Phone");
//Setup DragDrop Event Handler - is this correct, or even needed?
this.panel1.DragDrop += new DragEventHandler(panel1_DragDrop);
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
ListBox box = (ListBox)sender;
String selectedValue = box.Text;
DoDragDrop(selectedValue.ToString(), DragDropEffects.Copy);
}
private void panel1_DragDrop(object sender, DragEventArgs e)
{
//Change Background color to signify value has been dropped
((Panel)sender).BackColor = Color.Black;
}
}
I realize this is an oversimplified example. If you see what I am doing wrong, then please let me know.
EDIT To give an example of why I am confused, I change this example around to put the dragged ListBox Item text into a Textbox when the DragOver event was fired, and it worked fine, but when I tried to do the same thing when they dropped the values over the textbox, I could not get it to work.
Handle the panel's DragEnter event and set e.Effects to something other than None.