I have several UserControls that are sharing some common properties. Example:
private List<MyObject> Sample
{
get
{
return Session["MyObject"] as List<MyObject>;
}
set
{
Session["MyObject"] = value;
}
}
I want to share this to all user controls inside my project. (Not to other projects in a solution, of course). What I'm trying to do is create a separate class and inherit from that class. Something like:
public class SampleBase : Web.UI.UserControl
{
protected List<MyObject> Sample
{
get
{
return Session["MyObject"] as List<MyObject>;
}
set
{
Session["MyObject"] = value;
}
}
}
And then my control can inherit those values by deriving from that class:
partial class myControl : SampleBase
One problem I encounter is that I cannot derive from base if control already has something inherited:
partial class myControl : SomethingELSE
Otherwise it works fine, but I'm not sure if it is a good approach and I'm looking for suggestions.
If my understanding is correct, you only want to get rid of the inheritance hierarchy of your User Controls
Another approach would be using Extension Methods
For example:
Interface to mark your USerControls
public interface IMyUserControlMark { }
Extensions
public static class MyUserClassExtensions
{
public static List<object> GetSampleData(this IMyUserControlMark myUserControl)
{
if (HttpContext.Current.Session["MyObject"] == null)
{
return Enumerable.Empty<object>().ToList();
}
return HttpContext.Current.Session["MyObject"] as List<object>;
}
public static void SetSampleData(this IMyUserControlMark myUserControl, List<object> myObject)
{
HttpContext.Current.Session["MyObject"] = myObject;
}
}
User control
public partial class Content1 : System.Web.UI.UserControl, IMyUserControlMark
{
...
}
public partial class Content2 : System.Web.UI.UserControl, IMyUserControlMark
{
....
}
Now you will be able to call your extension methods from within your UserControl or from the ASPX code behind like this:
From the UserControl
var myObject = this.GetSampleData();
this.SetSampleData(myObject);
From the ASPX code behind
var myObject = this.uc1.GetSampleData();
this.uc1.SetSampleData(myObject);
This is a classic example where you need to "favor composition over inheritance".
Instead of inheriting from the class, you hold a reference to an instance of the class. Then you provide simple pass-through code to access the methods/properties of the class.
So, for your example:
public class SomeBehavior
{
public List<MyObject> Sample
{
get { return Session["MyObject"] as List<MyObject>; }
set { Session["MyObject"] = value; }
}
}
public class MyControl : UserControl
{
private SomeBehavior _someBehavior;
public MyControl()
{
_someBehavior = new SomeBehavior();
}
public List<MyObject> Sample
{
get { return _someBehavior.Sample; }
set { _someBehavior.Sample = value; }
}
}
Another option is to allow access to the behavior class directly:
public class MyControl : UserControl
{
public SomeBehavior SomeBehavior { get; private set; }
public MyControl()
{
SomeBehavior = new SomeBehavior();
}
}
The advantage of this is that you don't have to write the pass-through code. The disadvantage is that it violates the Law of Demeter, which says that you should "only talk to your immediate friends". If you do it this way, other classes that use MyControl need to know about SomeBehavior. Following the Law Of Demeter can improve maintainability and adaptability of your code, but it comes at a cost of lots of pass-through code.
Apart from previous solutions, maybe it's time for applying some MVC/MVP pattern?
For web forms there is a great framework called WebFormsMVP: link
In the library there is a mechanism called Cross Presenter Messaging thanks to which you can share a data between your controls using the publish/subscribe pattern.
For better explanation look here and here
I suggest to give the library a chance :)
In C# you can inherit from only one class and implement multiple interfaces.
This is allowed:
partial class myControl : SampleBase
partial class myControl : SampleBase, Interface1
partial class myControl : SampleBase, Interface1, Interface2, Interface3
This is NOT allowed:
partial class myControl : SomethingELSE, SampleBase
Try making SomethingELSE inherit from SampleBase if it satisfies your design. If not, then I suggest encapsulating SampleBase as a property of each control that needs it as it also suggested #DanM.
Related
I add an Usercontrol base class
my Base Class
public class UserControlBase : UserControl
{
protected IAppLogic app;
public MainWindow CurrentWindow{
get{
return (App.Current.MainWindow as MainWindow);
}
}
public UserControlBase()
{
var _app = IoC.Kernel.Get<IAppLogic>();
this.app = _app;
}
public void MainNavigate(Pages.PageBase p)
{
CurrentWindow.MainFrame.Content = p;
}
}
but the design does not shown
Browsing around some of the other questions, I found some of the reasons this can happen
Q1 WPF Designer “Could not create an instance of type”
Suround the code in your constructor with this:
if(!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
//code producing exception
}
The base class is of the abstract type
An exception is thrown in your constructor while loading the custom control. This goes back to 1.
Please share the stacktrace for us to help more.
I’m having a weird & frustrating problem passing an object between some of my classes. This stems from the fact I am a scripter and not a programmer, and am just bumbling along. So I’m sure I’m doing something dumb :)
I am trying to build a Wizard dialog which has multiple pages. I am using the “Internationalised WPF Wizard” tutorial from CodeProject as a starting point, and attempting to adapt it to my domain. I’m getting stuck because my wizard pages can’t seem to refer to the model.
I have done the following:
Created a class for my Model (let call this MyData)
Created a base class for my view models (ViewModelBase)
Created a view model class for each of my pages, inheriting from ViewModelBase (example below is WelcomePageViewModel)
Created a ‘controller’ style view model which drives the wizard. (WizardController)
When the wizard is launched, WizardController is instantiated. WizardController also instantiates MyData.Then, WizardController instantiates each of the view models for the remaining pages.
The actual GUI seems to work fine, and I can see that the view models for each of the pages are being loaded correctly. Here’s some code:
public class MyData
{
private string _someString;
public MyData(string someString)
{
_someString = someString;
}
}
public abstract class ViewModelBase : INotifyPropertyChanged
{
bool _isCurrentPage;
readonly MyData _myData;
public ViewModelBase(MyData myData)
{
_myData = myData;
}
}
public class WizardController : INotifyPropertyChanged
{
MyData _myData;
public WizardController()
{
_myData = new MyData("The Widgets");
}
}
public class WelcomePageViewModel : ViewModelBase
{
private MyData _myData;
public WelcomePageViewModel(MyData myData)
: base(myData)
{
_myData = myData;
// accessing _myData fails :(
MyLogger.WriteLine("Grabbed an instance of myData: " + _myData.ToString());
}
}
However, my code fails when I try to access myData from WelcomePageViewModel. On the MyLogger line in WelcomePageViewModel, the error “Object reference not set to an instance of an object.” is thrown.
Basically, all I’m trying to achieve is WizardController setting up MyData, and each of the wizard pages being able to access (and manipulate) it. So any guidance would be greatly appreciated!
As Rob G suggested in a comment, you're re-declaring the variable _myData in the inherited classes. The correct way to organize your code is to let the _myData be a protected property declared only on the abstract base class, and use this property to access the variable from the inheriting classes.
public abstract class ViewModelBase : INotifyPropertyChanged
{
bool _isCurrentPage;
protected MyData MyData { get; private set; }
public ViewModelBase(MyData myData)
{
MyData = myData;
}
}
public class WelcomePageViewModel : ViewModelBase
{
public WelcomePageViewModel(MyData myData)
: base(myData)
{
// Access the protected property
MyLogger.WriteLine("Grabbed an instance of myData: " + MyData.ToString());
}
}
Edit: fixed a copy-paste error...
In my prism application I want to make a single shared instance of a view. When I try to navigate the first time it works fine, but when I try to second time it's not working. If I change the PartCreationPolicy from Shared to NonShared it works but it's give me a new instance. Are there any options for another way to do this?
[Export(ViewNames.AppView)]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class AppMain : UserControl
{
public AppMain()
{
InitializeComponent();
}
}
You might want to play around with Prism's KeepAlive value for your view. This value determines whether the view should be removed from the region when you navigate away from it. You have two ways of doing this:
Using the RegionMemberLifetime attribute
[RegionMemberLifetime(KeepAlive = false)]
[Export(ViewNames.AppView)]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class AppMain : UserControl
{
public AppMain()
{
InitializeComponent();
}
}
Implementing the IRegionMemberLifetime interface
[Export(ViewNames.AppView)]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class AppMain : UserControl, IRegionMemberLifetime
{
public AppMain()
{
InitializeComponent();
}
public bool KeepAlive
{
get { return false; }
}
}
You can read some more about the KeepAlive property here.
My winform application contains usercontrols and forms.
Each usercontrol inherits a base usercontrol and each form inherits a base form.
The base usercontrol and the base form contain exactly the same properties and methods because what I want is to manage security and language settings at one single place.
This solution is working but I would like all my controls (usercontrols and forms) to inherit a unique base control in order to have these methods at one single place.
Is there a way to achieve this ?
Thanks !
Inheriting in this case doesn't make much sense from an OO perspective.
I suggest creating "SecurityManager" and "LanguageManager" classes and using them from your base form & usercontrol. They will have a clear role and let you control the logic from a single place.
I've run into this issue before. Unfortunately, since C# doesn't support multiple inheritance, your BaseForm and BaseUserControl cannot inherit from BaseFunctions, and Form and UserControl (respectively).
The work around I use is to create a IBaseFunctions interface with the methods needed and place the functionality in a helper class.
You still have a lot of copy-pasta, but at least it's just the wrapping and not the needed logic.
ex:
public interface IBaseFunctions
{
IUser GetCurrentUser();
void ShowMessage(String message);
}
public class BaseFunctionsHelper : IBaseFunctions
{
public IUser GetCurrentUser()
{
// Get Current User
}
public void ShowMessage(String message)
{
// Show message
}
}
public class BaseForm : Form, IBaseFunctions
{
private readonly IBaseFunctions _helper = new BaseFunctionsHelper();
public IUser GetCurrentUser()
{
return _helper.GetCurrentUser();
}
public void ShowMessage(String message)
{
return _helper.ShowMessage(message);
}
}
public class BaseControl : UserControl, IBaseFunctions
{
private readonly IBaseFunctions _helper = new BaseFunctionsHelper();
public IUser GetCurrentUser()
{
return _helper.GetCurrentUser();
}
public void ShowMessage(String message)
{
return _helper.ShowMessage(message);
}
}
I currently have this code, which I know is terrible, and I would like to refactor it as simply as possible.
if (control as HistoricalLineGraph != null)
{
dock.Title = string.Format(format, dock.ChartName, (control as HistoricalLineGraph).Title);
}
else if (control as HistoricalPieGraph != null)
{
dock.Title = string.Format(format, dock.ChartName, (control as HistoricalPieGraph).Title);
}
HistoricalLineGraph and HistoricalPieGraph both inherit from System.Web.UI.UserControl.
public partial class HistoricalLineGraph : System.Web.UI.UserControl
public partial class HistoricalPieGraph : System.Web.UI.UserControl
and they both have this getter:
public string Title
{
get { return Chart1.Titles[0].Text; }
}
Since getting the title relies on Chart1 being instantiated it isn't (easily) possible to extract this getter to a common base class. I believe I'd need to use composite controls to do such a thing, but I am not entirely positive as I have not explored that solution in-depth.
Is it possible to do something akin to creating a middle-man class that inherits from System.Web.UI.UserControl and that has an abstract getter for Title which is then overridden by each of my above classes? If not, what are my other options?
Thanks.
You can create an interface
public interface ITitledControl
{
string Title {get;}
}
and use
public partial class HistoricalLineGraph : System.Web.UI.UserControl, ITitledControl
public partial class HistoricalPieGraph : System.Web.UI.UserControl, ITitledControl
and do this
if (control is ITitledControl)
{
dock.Title = string.Format(format, dock.ChartName, (control as ITitledControl ).Title);
}
I would actually say that your best option is to create an interface which both controls implement. This means that you can have custom implementations if necessary, but simply cast the control to your interface and get the title.