My app root visual is a border panel which stays fixed the whole time.
What do changes is the Child of the border panel : I have made several screens (UserControl) and want to switch between them when certain buttons within each screen are clicked.
I want the transitions between the screens to be visually appealing so that each screen will have two StoryBoards , one for slide in animation and one for slide out animation. ( To make it clearer , The slide in animation of screen1 ,for example, will move screen1's buttons and fields in a specific way into the view area until they reach their final position ).
My goal is to achieve these screen transitions with a Behavior , and apply them to screens with as less code as possible ( Best will be XAML only).
First, I defiend a new Interface which all my app screens will implement :
public interface IScreenWithTransitions
{
void beginOutTransition();
void beginInTransition();
event EventHandler outTransitionEnded;
}
And then screen1 code behind will look like this:
public partial class Screen1 : UserControl, IScreenWithTransitions
{
public Screen1()
{
// Required to initialize variables
InitializeComponent();
}
public void beginOutTransition()
{
AnimationOut.Completed += outTransitionEnded;
AnimationOut.Begin();
}
public void beginInTransition()
{
AnimationIn.Begin();
}
public event EventHandler outTransitionEnded;
}
AnimationOut and AnimationIn are StoryBoards which I created with Blend for each screen.
Now, I want to write a Behavior to manage the transition. This Behavior will act on a Button. It will have two properties. One is OldScreenContainer which is of type Panel and represents the container of the screen we want to remove. Second is NewScreen which is of type IScreenWithTransitions and represents the new screen that we want to put inside the container.
public class SwitchScreensBehavior : Behavior<Button>
{
public static readonly DependencyProperty NewScreenProperty = DependencyProperty.Register("NewScreen", typeof(IScreenWithTransitions), typeof(ChangeButtonTextBehavior), null);
public static readonly DependencyProperty OldScreenContainerProperty = DependencyProperty.Register("OldScreenContainer", typeof(Panel), typeof(ChangeButtonTextBehavior), null);
public IScreenWithTransitions NewScreen
{
get { return (IScreenWithTransitions)GetValue(SwitchScreensBehavior.NewScreenProperty); }
set { SetValue(SwitchScreensBehavior.NewScreenProperty, value); }
}
public Panel OldScreenContainer
{
get { return (Panel)GetValue(SwitchScreensBehavior.OldScreenContainerProperty); }
set { SetValue(SwitchScreensBehavior.OldScreenContainerProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Click += AssociatedObject_Click;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_Click;
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
IScreenWithTransitions oldPage = (IScreenWithTransitions)OldScreenContainer.Children[0];
oldPage.outTransitionEnded += new EventHandler(oldPage_outTransitionEnded);
oldPage.beginOutTransition();
}
void oldPage_outTransitionEnded(object sender, EventArgs e)
{
OldScreenContainer.Children.Clear();
OldScreenContainer.Children.Add((UserControl)NewScreen);
NewScreen.beginInTransition();
}
}
For Example, lets say my root visual is called Border1 and is now containg a child called Screen1 which have a Button called Button1. when the button is clicked i want to switch to Screen2 , so i will put a SwitchScreensBehavior on Button1 with Border1 as OldScreenContainer property and Screen2 as NewScreen property.
This code compiles. I see this Behavior in Blend and can drag it onto a button. But how can i set the two properties of the behavior ? Blend shows me them but I can't figure how to point them to the new screen and to the container ( and what if the new screen is created in runtime) . So I tried to define those properties through the XAML , and again and don't know how to do it. Maybe define them in-code ?
Or maybe this functionality is to big for a Behavior and other elegant solution exists (which relies on as much XMAL as possible) ?
You can bind values of dependency properties in XAML, so what I'd do is to pick the Panel in Blend and bind the other screen via the ViewModel. So in your behavior you'd end up with something like:
OldScreenContainer="{Binding ElementName=MyPanel}" NewScreen="{Binding MyNewScreen}"
This is how you can bind in Blend after clicking on the small rectangle to the right of the property field:
To be able to reference a UserControl you might have to create a ViewModel and set it as DataContext, at least I can't think of any easier / cleaner way to do it at the moment. Actually, wait, do you have those screens added in your XAML? Could you perhaps paste in your main XAML to your question?
Let me know if that's clear or you need any clarification!
Related
I have a main form (form1) which has a panel (panel1) -- see pic.
Form1 pic
Panel1 loads one of two different user controls based on which button is pressed (to simulate screen changes). I have a button on user control 1 which needs to act (change text) on user control 2.
The issue I have is the user controls are dynamically created with a button press on form 1 (see code below) which is causing me issues trying to link events-
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
panel1.Controls.Add(new Screens.UC1());
}
private void button1_Click(object sender, EventArgs e)
{
foreach (Control ctrl in panel1.Controls)
{
ctrl.Dispose();
}
panel1.Controls.Add(new Screens.UC1());
}
private void button2_Click(object sender, EventArgs e)
{
foreach (Control ctrl in panel1.Controls)
{
ctrl.Dispose();
}
panel1.Controls.Add(new Screens.UC2());
}
}
What is the best way to deal with linking these kinds of items with events when the instance of the objects are dynamically created. I also tried making instances of the screen and then referencing to those, but that ran into scope issues.
Code for UC1 (user control 1)
public partial class UC1 : UserControl
{
public UC1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Event to change text on UC2
}
}
Code for UC2 (user control 2)
public partial class UC2 : UserControl
{
public UC2()
{
InitializeComponent();
}
public void WriteText(object sender, EventArgs e)
{
label2.Text = "Text Changed...";
}
}
Any help greatly appreciated.
Why dispose and create all those controls when the operator presses a button?
Better is to create two UserControls. One with all the Controls you want to show when operator presses button 1 and one with all the Controls you want to show when operator presses button 2.
To create a user control use menu Project - Add User Control, or right click in solution explorer on your project and select add new item.
Layout your user controls with all the controls your want to show. Add event handlers etc.
Then in your form:
private readonly UserControl userControl!;
private readonly UserControl userControl2;
public MyForm()
{
InitializeComponent()
this.userControl1 = new UserControlA(...);
this.userControl2 = new UserControlB(...);
// make sure that the user controls are Disposed when this form is Disposed:
this.Components.Add(this.userControl1);
this.Components.Add(this.userControl2);
}
void OnButton1Clicked(object sender, ...)
{
// remove userControl2 from the panel
this.Panel1.Controls.Remove(this.userControl2);
// add userControl1 to the panel
this.Panel1.Controls.Add(this.userControl1);
}
This way all the overhead of creating / adding / positioning / add event handlers and all cleanup is only done once: during construction of your form. Switching the user controls will be done in a flash
I am not sure what you are trying to accomplish, but it looks like you are trying to change the state of objects from other objects that cannot have references to the objects they are trying to change.
In this case, I would create a type that functions as some kind of manager that subscribes to events of all of these controls. You can create your own events within a UC class, or just use the Windows Forms click event like you are already doing.
Since the handler of the events are defined in the manager, you can easily write logic that will work on the other user controls, as long as the manager has references to them.
Like this:
public class ClickTrafficer {
private UC target;
public void HandleClick(object sender, UCClickHandlerEventArgs ea) {
target.WriteText(ea.TextToWrite);
}
}
public Form1()
{
InitializeComponent();
var trafficer = new ClickTrafficer();
var screen1 = new Screens.UC1();
screen1.Click += trafficer.HandleClick;
panel1.Controls.Add(screen1);
}
This is a crude idea of what you could do. Missing here are the logic to set whatever the target field must be set to. You need to create logic that tells the trafficer which control sets which control's text.
Also, the ClickTrafficer I created uses a custom event with custom eventargs, you need to define those or find a way to pass the necessary information through the built in events. Creating events is really easy though so you can look that up online.
I want change the small check-box color beside to item color in checklist in c#
public ColorControl()
{
KnownColor[] colors = Enum.GetValues(typeof(KnownColor)) as KnownColor[];
foreach (var item in colors)
{
this.Items.Add(item.ToString());
}
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
Color ItemColor = Color.FromName(this.Items[e.Index].ToString());
e.Graphics.DrawString(this.Items[e.Index].ToString(),this.Font,new SolidBrush(ItemColor) ,e.Bounds);
base.OnDrawItem(e);}
Not sure if this is worthy of an answer but here is a shot.
As is mentioned in the answer by Cody Grey The checkbox is not really simple to override. Another alternative besides making your own class inheriting from CheckListBox is to make your own control inheriting from Control. You can use buttons with changing background colors for the checkboxes. And add whatever other features you want.
Something like
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace CustomButton
{
public partial class NeatButton : Control
{
//Some globals
private bool _Pressed = false;
private bool _Activated = false;
//you will want to put your code for clicking checkboxes in the Mouse overrides. The OnPaint override is where you decide how the boxes look.
protected override void OnMouseDown(MouseEventArgs e){...}
protected override void OnMouseUp(MouseEventArgs e){...}
protected override void OnPaint(PaintEventArgs pe){...}
//You will want some propeties
public new string Text
{
get { return base.Text; }
set
{
if (value == base.Text)
return;
base.Text = value;
Invalidate(); //Keeps text showing changes in real time
}
}
/// <summary>
/// Works with Pressed to determine if the button should do something when clicked. **Use a property like this for the checkboxes**
/// </summary>
private bool Activated
{
get { return _Activated; }
set
{
if (value == _Activated)
return;
_Activated = value;
Invalidate();
}
}
/// <summary>
/// Works with Activated to determine if the button should do something when clicked
/// </summary>
private bool Pressed
{
get { return _Pressed; }
set
{
if (value == _Pressed)
return;
_Pressed = value;
Invalidate();
}
}
While this is not a complete example it should show a very basic set up for a custom control.
Once the custom control is working then want to build it and grab the exe from where ever you built it. Then in the form you want to use it in you drop the exe in to the project folder. Then in the solution explorer right click on References and then Add reference. Then browes to your exe and check it off and click 'OK'. Save your project and open up the form designer. Right click on the ToolBox and click Choose Items. Find your exe and check it off and hit ok. The Checkbox you made should now appear in the tool box and can be dragged and dropped onto a form and used like any other control.
Good luck.
The checkbox is drawn by the operating system. It is intentionally designed so that it looks like every other checkbox on the screen. This massively helps with usability. Users will not know what they can click on a red box and use it like a checkbox.
If you insist on making your application difficult to use, you will have to owner-draw the control. It will not be easy, CheckedListBox is not designed to support owner-drawing. The DrawItem event is documented as being "not relevant to this class." The control is already owner-drawn by the framework.
At the very least, you will need to create a new class that inherits from CheckedListBox so that you can override the OnDrawItem method.
public class ColorCheckBoxListBox : CheckedListBox
{
protected override void OnDrawItem(DrawItemEventArgs e)
{
// ...
}
}
I have created a custom Button for use in my WinForms applicationusing the followng little class
public class MyButton : Button
{
protected override void OnPaint(PaintEventArgs e)
{
this.BackColor = Color.ForestGreen;
base.OnPaint(e);
}
}
I'm simply looking to make my application cusomizeable so that I only need change button colors (and in due course other controls) in one place, and that change is reflected throughout the whole application.
After creating the custom button using the above code I set about replacing all standard System.Windows.Forms.Buttons() with MyNamespace.MyButton(). However, whilst the new buttons all appear changed on the screen, other controls like text boxes (which I have not modified) simply are not rendered on the screen at all. However if I click and drag a window in my application then all of the missing controls suddenly appear.
I have no idea what is causing this. Can anyone advise me please.
You shouldn't be "setting" the backcolor property in a paint event, that can cause a constant refreshing of the screen.
One option is to try setting the property in the constructor instead:
public class MyButton : Button
{
public MyButton() {
this.BackColor = Color.ForestGreen;
}
}
In order to ignore the serialized BackColor property of the control, you can try to change your button class to something like this:
public class MyButton : Button {
private Color myColor = Color.ForestGreen;
public MyButton() {
base.BackColor = myColor;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Color BackColor {
get { return myColor; }
set { // do nothing
}
}
}
This button control will effectively ignore the BackColor property in the designer. If you want to change the color of all of your buttons, you just have to change your myColor value in code and rebuild.
I am working on my first C# and .NET project that uses WinForms, as WPF would be an overkill for our purposes. I created a so-called ButtonMenu that comprises of all menu points, which are buttons. This class is derived from the Windows class Control.
One of the buttons of the ButtonMenu is the "Culture" button that, when pressed in this single-touch application, should change the language of all the forms the application has.
Originally, the ButtonMenu was just a kind of overlay class that accessed the controls of the BaseForm and contained methods. The BaseForm was holding the buttons inside a GroupBox of its own. Later, I run into problems with this kind of design and decided to make a separate control out of it.
My question
How can I create an event (or something similar to it) that can be caught by BaseForm, where the ButtonMenu is placed? The BaseForm can currently not react on this event and cannot change the language of all its own controls, such as text fields and buttons.
Thank you for your help!
What I have tried till now is shown below. Unfortunately, I cannot reach the marked line.
public class BaseForm : Form
{
[…]
protected static ButtonMenu m_ButtonMenu = null;
protected override void OnResize(EventArgs e)
{
[…]
m_ButtonMenu = ButtonMenu.GetInstance(m_CurrentCulture, Size);
m_ButtonMenu.Visible = true;
[…]
}
public override void UpdateWidgets()
{
[…]
try
{
[…]
// Translate button menu into current language:
m_ButtonMenu.AdaptButtons(m_CurrentCulture);
}
catch (ArgumentOutOfRangeException aaore)
{
[…]
[…]
}
protected void InitializeWidgets()
{
{
string strMethod = Name + ":InitializeWidgets(): ";
m_ButtonMenu = ButtonMenu.GetInstance(m_CurrentCulture, Size);
SuspendLayout();
Controls.Add(m_ButtonMenu);
m_ButtonMenu.Top = Height - m_ButtonMenu.Height;
ResumeLayout();
[…]
m_ButtonMenu.Click += new System.EventHandler(this.ButtonMenu_CultureClick);
}
private void ButtonMenu_CultureClick(object sender, EventArgs eas)
{
int iSelection = listViewMessages.SelectedIndices[0]; // <<<<< NEVER REACHED!
[…]
}
Just define an event in your class.
Whenever you want it to fire, call it.
// field
event EventHandler somethingHappened;
// in a method:
var threadSafeCopy = somethingHappened;
if(threadSafeCopy != null)
{
threadSafeCopy(this, e);
}
Where e is an instance of EventArgs or a sub type of EventArgs.
I'm using .NET C# with standard WinForms, not WPF.
I have this situation.
I'm creating a user control for a month calendar, similar to the .NET one but with a little more functionality.
I have a user control form, that fills with button objects representing dates.
The buttons can be colored with different color depending on their state(selected, mouse over, weekend...)
The way I'd like it to work is extending the button class to accept states, which determine colors, rather than coloring them from the parent (user control) class. There are 10 colors at the moment and I'd really wouldn't like to mess up the user control code with coloring conditions.
Also I would like to select all the colors at design time, using browsable designer properties.
The problem is that the designer shows only properties defined in the user control class, and not its children (buttons).
Is there any workaround for this problem?
So to put it short I want to change colors using internal button properties, and to be able to select them at design time, using designer properties, and not hard coding them manually.
Ok, I'll try to explain trough code:
For example, I have a user control and a button class.
I want to expose Button properties, and make them visible among MyControl properties in designer.
class MyControl : UserControl
{
private MyButton button;
button.ChangeStyle("Selected");
}
class MyButton : Button
{
private Color buttonColor;
public void ChangeStyle(string styleName)
{
if (styleName == "Selected")
this.BackColor = buttonColor;
}
[Browsable(true)]
[Category("Button style")]
public Color ButtonColor
{
get { return buttonColor; }
set { buttonColor = value; }
}
}
This is a simple example. Normally I have 5 different styles including background and foreground color for each of them. So instead of managing colors in MyControl class, I'd like to define them in MyButton class. But the problem this way is that the properties in the MyButton class aren't visible in designer, because it only focuses on MyControl properties.
Btw. ignore the missing constructors and other basic classes stuff in the code example
I can't use:
[Category("Wonder Control")]
public Color ButtonBackColor { get { return button.BackColor; } set { button.BackColor = value; }
because I have 30 buttons in MyControl (days in month), and I can't reference just a single object.
For a property to be visible in the designer, they have to be public properties with a getter and setter - from what you're saying, the properties are only getters. You could also try specifying BrowsableAttribute and BindableAttribute on the properties to coerce the designer to display them...
There are various things you can do here - you could (although it is a bad answer) expose the controls in question on the public interface - but I'm not sure that is a great idea.
Personally, I would just re-expose the properties I am interested in, perhaps putting them into a different [Category(...)] - making sure to have both setters and getters.
A bit like:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
class MyControl : UserControl
{
private Button button;
private Label label;
public MyControl()
{
button = new Button { Dock = DockStyle.Right, Text = "Click me" };
label = new Label { Dock = DockStyle.Left};
Controls.Add(button);
Controls.Add(label);
}
[Category("Wonder Control")]
public string CaptionText { get { return label.Text; } set { label.Text = value; } }
[Category("Wonder Control")]
public Color ButtonBackColor { get { return button.BackColor; } set { button.BackColor = value; } }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (Form form = new Form())
using (MyControl ctrl = new MyControl())
using (PropertyGrid grid = new PropertyGrid())
{
ctrl.ButtonBackColor = Color.Red;
ctrl.CaptionText = "Caption";
ctrl.Dock = DockStyle.Fill;
grid.Dock = DockStyle.Right;
form.Controls.Add(ctrl);
form.Controls.Add(grid);
grid.SelectedObject = ctrl;
Application.Run(form);
}
}
}
If all of the buttons within the control will share the same appearance, why not put the property at the control level and have the property setter propogate any changes to all of the buttons? Also, using 30 individual button controls seems like lot of overhead... have you considered drawing the labels for the days and handling mouse click/hover events to determine when a particular day is clicked?