I have a Class that contains: A DataGrid, A Pager
I am trying to make User Control that contains both of them and have all property that links to both of them.
So something:
public class MyGridViewPager : ltcGrid
{
public GridView aGridView;
public DataPager aDataPager;
public bool AutoGenerateColumns {
get { return this.aGridView.AutoGenerateColumns; }
set { this.aGridView.AutoGenerateColumns = value; } }
}
So I can use the property from them in my MyGridViewPager UserControl and in case of any choice change on the GridView or DataPager I can modify only the layer...or is it called wrapper-class? Not so sure about the naming...
But... how do I do the same thing for EventHandler?
I can see it defines in:
public delegate void SelectionChangedEventHandler(object sender, SelectionChangedEventArgs e);
How can I expose this handler from my user control?
Note: I don't really want to do MyGridViewPager.aGridView.SelectionChangedEventArgs.
There is a myriad of ways to do this:
Exposing events of underlying control
Event Bubbling in C#
Related
I have a WinForm that I have used to build and test an email newsletter. This form contains a number of methods and events.
I am now adding a new feature to my program to allow split testing (A/X Test) and therefore creating up to 4 different newsletters for a campaign.
Therefore I'd like to extend my form to accommodate both normal newsletters and A/X newsletters. I'm going to add two different modes to my form. Something like:
private enum CampaignMode { Normal, AxTest };
They will be very similar in appearance, except a number of controls' visibility will change.
Apart from that, almost all methods and events will have two separate ways of handling.
What design pattern should I use so that I don't have to create a new separate form?
For simplicity, let's say my form has the following methods:
Constructor: probably receiving the EditMode as a parameter
Load
Create: Button click event
SetControlViews: Based on EditMode set the visibility of controls
MethodA: Specific to Normal mode
MethodB: Specific to AxTest mode
Sounds like Template Method would be a good candidate.
Make a base class which takes care of logic for the basic initialization, Load(), Create(), SetControlViews() and then make Method() virtual and override in two derived classes according to specialized logic. Also, the constructor of each derived class could do some specialized initialization.
This way you don't even need a CampaignMode enum (or EditMode if you meant those to be identical). The existence of different entities there is illustrated by the existence of different derived classes.
I would simply have pairs of functions for each method/event and then have the main handler call the appropriate one, depending on the value of CampaignMode.
public enum CampaignMode { Normal, AxTest };
public partial class DemoForm : Form
{
private CampaignMode campaignMode;
public DemoForm(CampaignMode mode)
{
InitializeComponent();
campaignMode = mode;
SetControlsVisibility();
}
private void SetControlsVisibility()
{
if (campaignMode == CampaignMode.Normal)
{
//Set normal controls visible;
//Set axtest controls invisible;
}
else
{
//Set normal controls invisible;
//Set axtest controls visible;
}
}
private void button1_Click(object sender, EventArgs e)
{
if (campaignMode == CampaignMode.Normal)
{
MethodA();
}
else
{
MethodB();
}
}
private void MethodA()
{
}
private void MethodB()
{
}
}
Note: if there are many controls, then it is neater to have a visibility function with a bool parameter:eg
private void SetNormalVisibility(bool isNormal)
{
//normalTextBox.Visible = isNormal;
//normalButton.Visible = isNormal;
//axTestTextBox.Visible = !isNormal;
//axTestButton.Visible = !isNormal;
}
In which case, change the SetControlsVisibility function to:
private void SetControlsVisibility()
{
if (campaignMode == CampaignMode.Normal)
{
SetNormalVisibility(true);
}
else
{
SetNormalVisibility(false);
}
}
HTH
Jonathan
I need to write a custom control that looks like a TextBox and that contains a method called Refresh() which main purpose will be to clear the Text and to roll back few other values.
The method shall become bindable somehow so that others can bind a property from their ViewModel with it. Hence why I am thinking that inside my custom control I will need an dependency property of type Action.
So far so logical but next problem is the method/dp may no get overriden on control side once users sets a two way binding on it. Basically I always have to deliver the method wrapper as Action to ViewModel and inside ViewModel other users may call it.
How do I do all this? It seems to me that I have to somehow get the binding of the method work like OneWayToSource.
I apologize in case its a duplicate. Futhermore thanks in advance guys.
EDIT: Please no alternative solutions. Those are the requirements and I have to stick to them.
I think that the simplest thing you can do here is to expose a bool property, maybe called IsCleared, and just call your method from that property when it becomes true. Exposing ICommand and/or delegate objects transfers the functionality out of your control, so you can't use those.
#ninjahedgehog, why can't you use a bool 'switch' property? Your requirement says 'so that others can bind a property from their ViewModel with it'... they can bind to a bool property from their view model. In my opinion, it seems to be your only option. As I said earlier, you can't use ICommand and/or delegate objects as that would transfer the functionality out of your control - that would enable other developers to write their own functionality rather than to just call yours.
What you really want is a method on your control that they could call from their view model... but view models shouldn't have any knowledge about the view controls, so you can't do that. The next best thing to that is creating a method that is called when a property is given a certain value. Here you have a few choices.
If you really don't like the bool switch idea, then how about an enum property? Create an enum with specific values like ClearText and whatever other functionality you would like to expose. Then the other developers simply set this property to the relevant instance to instantiate that functionality... I only suggested the bool switch property because it seems as if you only want to expose one piece of functionality.
One last point to note about using the bool switch property... as it is a switch, you need to reset it after use, or just never actually set it:
public bool IsTextClear
{
get { if (value) ClearText(); }
}
I dont know why you need this coz the person who is using your control can directly call the method from the code behind. But if you want that there should be some property like ClearMe on control and when set to true it should clear the control then you can define the dependency property and listen to its change in control like below and call Refresh from there.
public static readonly DependencyProperty ClearMeProperty = DependencyProperty.Register
(
"ClearMe",
typeof(bool),
typeof(MyControl),
new FrameworkPropertyMetadata(false, OnClearMeChanged)
);
public bool ClearMe
{
get { return (bool)GetValue(ClearMeProperty); }
set { SetValue(ClearMeProperty, value); }
}
private static void OnClearMeChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as MyControl;
if((bool)e.NewValue)
{
control.Refresh()
}
}
and you can bind this property to your ViewModel property. whenever ViewModel property will change to true. Property Change will be fired in control and will refersh it.
I editted my answer, as I wasn't understanding what you wanted. The only way I could come up with to do what you want was to use an Action DependencyProperty on the CustomControl and bind that to the ViewModel using a OneWayToSource binding, that way the Action from the control gets sent to the viewmodel. Within your customcontrol, you can test to make sure that only OneWayToSource binding is used and do something if not.. in this case, I add some text and made the background red.
View
<UserControl x:Class="WpfApplication1.Views.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
xmlns:controls="clr-namespace:WpfApplication1.Controls">
<UserControl.Resources>
<vm:TestViewModel x:Key="TestViewModel" />
</UserControl.Resources>
<StackPanel DataContext="{StaticResource TestViewModel}">
<StackPanel Orientation="Horizontal" Height="30">
<controls:CustomTextBox Width="300" Refresh="{Binding RefreshAction, Mode=OneWayToSource}" />
<Button Content="Refresh" Width="80" Command="{Binding RefreshFromView}" />
</StackPanel>
</StackPanel>
ViewModel
using System;
using System.ComponentModel;
namespace WpfApplication1.ViewModels
{
public class TestViewModel : INotifyPropertyChanged
{
public TestViewModel()
{
RefreshFromView = new RelayCommand(ExecuteRefreshFromView);
}
public Action RefreshAction { get; set; }
public RelayCommand RefreshFromView { get; set; }
private void ExecuteRefreshFromView(object parameter)
{
if (RefreshAction != null)
RefreshAction();
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyOfPropertyChange(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Custom Control
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace WpfApplication1.Controls
{
public class CustomTextBox : TextBox
{
public CustomTextBox()
{
this.Loaded += CustomTextBox_Loaded;
}
void CustomTextBox_Loaded(object sender, RoutedEventArgs e)
{
BindingExpression bindingExpression = GetBindingExpression(RefreshProperty);
BindingMode mode = bindingExpression.ParentBinding.Mode;
if (mode != BindingMode.OneWayToSource)
{
Text = "Use OneWayToSource Binding only!";
Background = new SolidColorBrush(Colors.Red);
}
Refresh = new Action(DoRefresh);
}
private void DoRefresh()
{
Text = null;
}
public Action Refresh
{
get { return (Action)GetValue(RefreshProperty); }
set { SetValue(RefreshProperty, value); }
}
public static readonly DependencyProperty RefreshProperty = DependencyProperty.Register("Refresh", typeof(Action), typeof(CustomTextBox));
}
}
You could use a Command:
public class Command : ICommand
{
public void Execute(object parameter)
{
// Do whatever you have to do
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
In your ViewModel:
public ICommand Command { get; set; }
In your XAML (assuming that your Custom Control is composed of a TextBox and a Button for example):
<Button Click="{Binding Command}" />
I have a listBox that I would like to carry out a method when its loaded up, although I can't use the Form "On_Load" trigger, since the ListBox is within a TabControl.
Is there a way of getting a method to execute when the object initializes?
The closest analog for controls is the HandleCreated event. This will fire when the underlying control handle is created, which is slightly before the Loaded event of the parent window would fire.
As #SLaks stated, you could put in your class's constructor. However, if what you want to prepare relies on other elements in the form, you can add to the event handler queue at the end of a form loading, but before its actually presented to the user.
In the constructor code of your form (not the designer code), add to the load event, then add your own custom function
public partial class frmYourForm : Form
{
public frmYourForm()
{
Load += YourPreparationHandler;
}
private void YourPreparationHandler(object sender, EventArgs e)
{
// Do you code to prepare list, combos, query, bind, whatever
}
}
Have the same problem, the previous answers apply well, for a single case.
But, I require to do something in most controls, in several forms, in an app.
Solved by using an interface:
interface IOnLoad
{
void OnLoad();
}
And added to descendant control:
public partial class MyButton : Button, IOnLoad
{
void OnLoad() { // call "OnLoadDelegate" }
}
public partial class MyForm : Form
{
public void MyForm_Load(...) {
foreach(Control eachControl in Controls) {
if (eachControl is IOnLoad) {
IOnLoad eachOnLoadControl = (IOnLoad)eachControl;
eachOnLoadControl.OnLoad();
}
} // foreach
}
} // class
Its more complex, but it suit my requirements.
You can use OnHandleCreated(EventArgs e). However, it triggers during design time too. You can override it too.
Can you use the HandleCreated event?
You can just put your code in the constructor.
You usually don't need to wait for any initialization.
I have a control that handles commenting. In this control, I have set a delegate event handler for sending an email.
I then have various types of controls, e.g. blog, articles etc, each of which may or may not have the commenting control added (which is done dynamically with me not knowing the id's), i.e. the commenting control is added outside this control. Each of these controls handles it's emailing differently(hence the event).
What I'm trying to determine, is how to assign the event in the parent control. At the moment, I'm having to recursively search through all the controls on the page until I find the comment control, and set it that way. Example below explains:
COMMENTING CONTROL
public delegate void Commenting_OnSendEmail();
public partial class Commenting : UserControl
{
public Commenting_OnSendEmail OnComment_SendEmail();
private void Page_Load(object sender, EventArgs e)
{
if(OnComment_SendEmail != null)
{
OnComment_SendEmail();
}
}
}
PARENT CONTROL
public partial class Blog : UserControl
{
private void Page_Load(object sender, EventArgs e)
{
Commenting comControl = (Commenting)this.FindControl<Commenting>(this);
if(comControl != null)
{
comCtrol.OnComment_SendEmail += new Commenting_OnSendMail(Blog_Comment_OnSendEmail);
}
}
}
Is there an easier way?
EDIT:
The reason I ask is that if I search from this.Page as the initial control, I am worried about time taken to search down the control tree to find it. Each different type of page would be different in how many control it would have. On some testing, it returns back quite quickly the result.
You could override the AddedControl event of your Blog class and check if the added control is instance of type Commenting. Something like:
public partial class Blog : UserControl {
protected override void AddedControl(Control control, int index) {
base.AddedControl(control, index);
Commenting commentingControl = control as Commenting;
if (commentingControl == null) return;
commentingControl.OnComment_SendEmail += new Commenting_OnSendMail(Blog_Comment_OnSendEmail);
}
}
Of course, you can put this code on a base class of all your "commentable" user controls and have an abstract method to actually handle the event.
Just one thing: the AddControl event happens AFTER the Page_Load, so be careful.
Cheers,
André
I have a gridview and usercontrol on the page, I want to alter gridview from usercontrol. how can i do that?
And also how can I call functions in usercontrol's "host" page from the usercontrol?
UPDATE:
I have a dropdownlist inside this usercontrol, when the it's SelectedIndexChanged event is triggered i want the gridview on the host page to DataBind().
(Then, gridview using objectdatasource will read this dropdownlist selected item and use it as select parameter)
Also if it possible to make it as general as it possible, because the altered control is not always a gridview...
Thanks
First of all, you would not want to do neither. That's against the concept of user control.
If you want to modify a gridview using the user control's state, you can expose the control's state and make the gridview to behave according to this state.
// This handles rowdatabound of gridview
OnRowDataBound(object sender, RowDataBoundEventArgs e)
{
var control = e.Row.Find("UserControlId");
if (control.SomeProperty == SomeValue)
someTextBox.Value = "something";
}
If you really need to pass a handle of gridview to a user control, define a property on the user control of grid view type:
// This is a property of user control
public GridView Container { get; set; }
and set the control's container to the gridview, before accessing it.
userControl.Container = gridView;
If the user control is a part of the itemtemplate on the grid, since your user control is created while rows of the gridview are created, you can only this after you bind your gridview.
Finally, for calling a function on the container page, you can expose an event inside of your user control and bind to that event.
public delegate void SomethingHappenedEventHandler(object sender, EventArgs e);
// In user control:
public event SomethingHappenedEventHandler SomethingHappened;
// Trigger inside a method in user control:
SomethingHappenedEventHandler eh = SomethingHappened;
if (eh != null) eh(this, EventArgs.Empty);
// In page:
userControl.SomethingHappened = new SomethingHappendEventHandler(OnSomething);
private void OnSomething(object sender, EventArgs e)
{
// When something happens on user control, this will be called.
}
Simply add a public method to your usercontrol's code-behind, then you can call this from the page and pass the gridview as a parameter:
public class MyUserControl : UserControl
{
public void MyMethod(GridView gridview)
{
// do stuff with the gridview
}
...
}
The second question is a bit more complicated. Because if you make your usercontrol depend on a specific page, it will no longer be reusable on other pages, e.g:
public class MyUserControl : UserControl
{
public void AnotherMethod()
{
//get the current page and cast it to its type
MyPage page = this.Page as MyPage;
// now I can call the public methods of MyPage
page.SomeMethod();
}
...
}
A better solution would be to define an interface, which is implemented by the page. Then you can methods of that interface by casting the current page to the interface (and the controls can still be reused on other pages implementing the same interface):
public interface IMyInterface
{
void SomeMethod();
}
public class MyPage : IMyInterface
{
public void SomeMethod() { ... }
}
public class MyUserControl : UserControl
{
public void AnotherMethod()
{
//get the current page and cast it to the interface
IMyInterface page = this.Page as IMyInterface;
// now I can call the methods of the interface
if (page != null) page.SomeMethod();
}
}
EDIT: In your user control, simply create a public property to hold the grid. That property will then hold a reference to grid in the host page. For example, you could add the following to your UserControl's code behind.
public string CurrentGridID
{
get;
set;
}
public void ModifyGrid()
{
//make changes to the grid properties
GridView grid = Page.FindControl(CurrentGridID);
grid.AutoGenerateColumns = false;
}
From your the page hosting the control, you could do something like this.
protected void Page_Load(object source, EventArgs e)
{
this.myUserControl1.CurrentGridID = this.gridView1.ID;
}
You should expose a public property that accepts the id of the server control you want to reference. You could then grab that control by querying Control.NamingContainer. You should not query Page, since you want to grab the control in your current context (you could be in a usercontrol with controls named the same as the page).
Dont forget the often overlooked IDReferencePropertyAttribute. It tells your designhost (usually Visual Studio) to provide autocompletion for the value using all controls in the current context.
To call stuff on your containing Page, just use the Page property. You need to cast this to your specific page class if you need to access your own methods.