Display arbitrary controls on a WPF Window - c#

I'm developing a WPF wizard WiX custom managed bootstrapper application.
Some elements of the wizard are common (for example, the product information and logo at the top), and I don't want to have to redefine these. I also don't want to have to redevelop the whole thing from scratch for each install (yes, there are a number of installs I want to use this with and they have different wizards).
I have a model like this (simplified to avoid confusion)
public class WizardModel : INotifyPropertyChanged
{
private UserControl _currentPage;
public UserControl CurrentPage
{
get { return _currentPage; }
set
{
_currentPage = value;
if (PropertyChanged != null) PropertyChanged(this, "CurrentPage");
}
}
}
I would like to bind a control so that the control in CurrentPage is displayed when the property changes.
I did experiment with using ContentCrontol but I quickly dismissed that as not intended for this purpose, and I'm really not sure where to go from here.

I'm making a few guesses here because there's not a lot of detail in the original question, but I think I get the idea of what you are trying to accomplish.
Seems like you would want to have a base class with all of the common controls on it already. Say we call this class WizardUserControl. Anyway, let's say we define a Grid inside the WizardUserControl (let's call it CustomGridArea) and that grid will be the main area where we can drop all of our custom controls based on which page of the wizard we are processing. So maybe WizardUserControl may implement a method like:
public class WizardUserControl : UserControl
{
...
// All your standard wizard code stuff/behavior/business logic/etc...
...
...
public void InsertCustomizedControl(UserControl customizedControl)
{
CustomGridArea.Children.Clear();
CustomGridArea.Children.Add(customizedControl);
}
}

Related

How to refactor duplicated code in Windows Forms?

I'm currently working on refactoring a lot of duplicated code in a couple of UserControls in Windows Forms project.
The architecture leaves much to be desired with a lot of logic implemented inside the UI layer. This will be fixed eventually, I'm now working on improving what I have on my hands now.
The problem is, that a lot of duplicated code relates directly to controls, for instance:
private void InitDestinationPathControls(string path)
{
if (someField)
{
tbOne.Enabled = false;
tbOne.Visible = false;
btnTwo.Enabled = false;
btnTwo.Visible = false;
tbOne.Text = string.Empty;
return;
}
// (...)
}
Don't get too attached to the cited code itself, it is just an example.
I'd like to move this code to a common base class, but it relies directly on specific fields (even though they are exactly the same in all controls too). Those fields, on the other hand, are generated by the designer, so I cannot extract them to the base class.
The only thing that comes to my mind is to pass those fields as parameters to a method in base class, but then if some method uses a lot of them, I'll end up with a monstrous interface part and that won't actually improve the readability too much.
How can I deal with such common parts of user controls in Windows Forms?
Apparently you have a combination of several controls that appears in several forms. In other words: you have for instance some buttons, comboboxes, etc, that you want to show on different forms, and you want them to have the same behaviour.
If not only the behaviour of these controls is the same on all forms, but also the layout, then consider to create a class derived from UserControl.
The UserControl hides from the outside world which controls are shown, how they are visualized and how they behave.
I assume that you already pulled the model out of the visualisation of the data.
If all instances of this user control should all call the same functions of possibly a different object of the same class, then give your special user control a property that represents this class, or at least an interface. During construction you can plug in the actual class that should handle the actions after operator input.
If, on the other hand, the layout differs on each form, but the collection of buttons, comboboxes, etc and their behaviour is similar on all forms that show this collection of controls and they have a lot of common behaviour, consider to create your own ControlCollection.
For instance, if on several forms you have a button to select a (text) file, labels with the name, size and creation date of the selected file, and an edit box that shows the content of the text file, but you want to layout them differently, consider something like this:
class FileDisplayControls : IDisposable
{
public Button ButtonSelectFile {get;} = new Button();
public Label labelFileName {get; } = new Label();
public Label labelFileSize {get; } = new Label();
public TextBox textFileContents {get; } = new FileContents();
private void ButtonSelectFile_Clicked(object sender, ...)
{
// TODO: open file dialog, display result in labels and text box
}
}
Constructor can set initial layout properties of the controls, and subscribe to events, such that the controls will react on user input.
The user of the class (= code, not operator) immediately has a collection of controls that have some standard behaviour, like react on button click. All he has to do is set the location of the items in his own form. If desired change other layout properties (colour, background) and put them on his own form.
If you want to prevent that others change other visual aspects of the controls than the position, don't publish the control themselves, only the position of the control:
public System.Drawing.Point LocationSelectFileButton
{
get => this.buttonSelectFile.Location;
set => this.buttonSelectFile.Location = value;
}
public System.Drawing.Point LocationFileContentTextBox
{
get => this.textBoxFileContent.Location;
set => this.textBoxFileContent.Location = value;
}
etc.
If needed, you can add events for users:
public event EventHandler SelectedFileChanged;
public string FileName => this.labelFileName.Text;
public string FileContents => this.textBoxFileContent.Text;
etc.
Conclusion
The solution that you choose depends on the similarity between the controls on the various forms:
if Behaviour and Layout are all the same: UserControl
If only position and a few properties different: special class with the properties that are different. This way you can force a more similar style: all "Select File" buttons look similar.
If only one or two behaviours are different: add Action<...> properties or events
If you want full control of the layout: expose the Controls.
The behaviour that is common for all you forms that show these controls (in my example: how to select a file and what to do when a file is selected) is inside the class.
repeated code can be extracted to method (possibly in base class, or as static method in helper class)
public void DisableControls(params Control[] controls)
{
foreach(var c in Controls)
{
c.Enabled = false;
c.Visible = false;
if (c is TextBox t)
{
t.Text = string.Empty;
}
}
}
private void InitDestinationPathControls(string path)
{
if (someField)
{
DisableControls(tbOne, btnTwo);
return;
}
// (...)
}

UWP app: insert / remove user control MVVM way

I'm learning UWP at the moment in an attempt to port an old Win32 to the new platform. I'm using Template10 and everything runs fine so far, except I'm bit confused on how to implement the problem below.
Problem: In a page, I have to constantly remove and insert user controls depending on a view model property. The user controls are fairly complex and they all look and behave differently. Imagine a wizard with back and next buttons. On every click I have to remove the old content and insert a new one, with completely different view model.
Question: What would be the recommended way of implementing this in a MVVM way?
At the moment, my only idea is to send a message from the page's view model and subscribe for the message in page's code behind where I can create the required component and insert it dynamically in the page (after removing the old one).
In MyPageViewModel:
public IComponentViewModel CurrentComponent {get; set;}
...
public DelegateCommand NextItemCommand = new DelegateCommand(() =>
{
var evt = App.EventAggregator.GetEvent<ItemChangedMessage>();
evt.Publish(CurrentComponent);
});
In MyPage.xaml.cs code behind
public MyPage()
{
InitializeComponent();
var evt = App.EventAggregator.GetEvent<ItemChangedMessage>();
evt.Subscribe(OnItemChanged);
}
private void OnItemChanged(IComponentViewModel viewModel)
{
switch (viewModel.Type)
{
case 1:
// create the new user control and insert it in the container
var component = new TypeOneComponent();
component.DataContext = (TypeOneCompoentViewModel)viewModel;
// ...
case 2:
...
}
}
Not sure this is the best approach tho.
I've been thinking about a Wizard approach lately myself. It seems to me that a FlipView with re-templated left/right buttons is the easiest approach. My WizardViewModel would have several children view-models; something like Page1ViewModel, Page2ViewModel, and so on. I strongly feel that each page view-model would have a dedicated UserControl so the UI can be unique but not dynamic - I think it makes sense to design against dynamic UI, while embracing an adaptive UI - which is a different concept altogether.
The pseudo code might look like this:
public interface IWizardPage { }
public class Page1ViewModel : ViewModelBase, IWizardPage { }
public class Page2ViewModel : ViewModelBase, IWizardPage { }
public class Page3ViewModel : ViewModelBase, IWizardPage { }
public class MainPageViewModel : ViewModelBase
{
public IWizardPage CurrentPage { get; set; }
public IWizardPage Page1ViewModel { get; set; }
public IWizardPage Page2ViewModel { get; set; }
public IWizardPage Page3ViewModel { get; set; }
}
And this:
<FlipView Template="{StaticResource WizardFlipView}"
SelectedItem="{Binding CurrentPage, Mode=TwoWay}">
<Page1UserControl DataContext="{Binding Page1ViewModel}" />
<Page2UserControl DataContext="{Binding Page2ViewModel}" />
<Page3UserControl DataContext="{Binding Page3ViewModel}" />
</FlipView>
This is just a recommendation. But to answer your question, this would be very amenable to the MVVM pattern. I also think this would allow for you to be very flexible without getting so dynamic that maintenance is affordably time consuming. There are lots of ways to do wizards. I think this would be a fine solution, good with the MVVM pattern and fine with Template 10.
Best of luck.
I typically use an ItemsControl. This allows you to have a generic item template and a item specific template if you want and you can add / remove items at will by binding to the ItemsSource.
In your example of a wizard, you might make the main wizard container an ItemsControl that only shows one item at a time and a page would be an "item". The distinction with MVVM is that you don't add child controls, you add data and then specify a template to render it. So your items are going to be simple databound poco objects.
For your actual example, I guess you can add child controls to the ItemsControl and they would render automatically without even using a template since a ContentPresenter knows how to render controls. I would still use data only classes though since one of the tenants of MVVM is to separate the data from the UI. So your child item would be a model and your item specific template would be the UI layout bound to the item data.

How to make static class which updated itself? (Similar to Mouse)

Mouse, as you know it, is a static class. It has a static property called Mouse.Position. That property, is updated dynamically (binded to mouse, maybe??). (I've read the Mouse documentation in msdn)
I need to build a same class with this scenario.
I am making an application which able to have multiple pages, and each of the pages shared, for example, Margin value.
There're 2 options (I think) to do it :
Make a field in "page" class, and pass the value via constructor (easiest)
Static class, similar to Mouse.Position usage, but, in this case, for example, PageInformation.Margin (PageInformation is a class, not a property nor a field)
I prefer the second option since it's easier to debug (i guess), and make the Page class cleaner, codeless, and easier during update if the Margin somehow changed by the user..
Anyone know how to do it? (mvvm way preferred)
Thanks.
UPDATE :
P.S. I already understand the basic mvvm practice (INPC and such)
This is the general code so far in my application :
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...
}
public class SheetPage : ViewModel
{
...
}
Your class will be a Singleton and therefor next to impossible to unit test. That does not seem to be a very practical solution.
In WPF you can bind all margins of all pages to the same ViewModel property. This way, when your ViewModel property changes (assuming you properly implemented INotifyPropertychanged or used DependencyProperty) all your margins will change as well.

How to create a 'style master' winforms control

I have an application with several DataGridView controls. I would like to create a master control or component (I'm not sure which one to use) where I can define basic coloring and behavior. However, since all DataGridView controls have different columns in the application I want to define columns after placing a new instance on the form and not in the master form.
Can you suggest a way to do this? Thanks in advance.
Have something like your own MasterGridView where you define all common behavior, colour, etc.. & then inherit all respective datagridviews from this one.
public class MasterDataGridView: System.Windows.Forms.DataGridView
{
public MasterDataGridView()
{
BackColor = Color.Yellow;
// define other behaviours
}
}
public class OrdersDataGridView : MasterDataGridView
{
// data binding, column addition etc can be handle in respective grid views
}
public class ReportsDataGridView : MasterDataGridView
{
}
...
etc.

Inheriting from a System.Web.UI.UserControl base class

First off, I am wondering if this is possible. I read slight grumblings around the internet about this, but I was not entirely sure.
My scenario: I have a base chart class which has some methods which all charts should have.
public partial class BaseChart : System.Web.UI.UserControl
{
public BaseChart()
{
}
public void ToggleLegend()
{
Chart1.Legends[0].Enabled = !Chart1.Legends[0].Enabled;
}
}
There is also some mark-up for this BaseChart -- setting background colors, etc. All charts which inherit BaseChart should use this starting mark-up and be able to build upon it.
I would then like to do this:
public partial class HistoricalLineChart : BaseChart
{
public HistoricalLineChart()
: base()
{
}
public HistoricalLineChart(int reportID)
: base()
{
Chart1.Titles[0].Text = "Hello World";
}
}
where HistoricalLineChart is a web user control with no mark-up e.g. "HistoricalLineChart.ascx"
The problem is that Chart1 is undefined when in HistoricalLineChart's scope. Is there something that I am missing here?
Thanks.
Though usually one ends up just making a custom control in this situation (as the other answer suggest), and this is good for many reasons, there are a couple other approaches that may be useful in situations where complicated markup makes a server control infeasible.
1) Create a base class that has all the functionality common to your implementations and inherits UserControl. If you really want to include markup as part of the base class, you could put it in a separate usercontrol with no code, and load it from the abstract class, though this seems a little ugly. If the markup that is shared is simple, though, just render it from code instead.
public abstract class MyUserControl: UserControl
{
public Chart Chart1;
public void ToggleLegend()
{
Chart1.Legends[0].Enabled = !Chart1.Legends[0].Enabled;
}
public override void CreateChildControls()
{
Controls.Add(Page.LoadControl("path/to/mymarkup/control"));
// or add them in code
BuildBaseControls();
}
}
2) Create UserControl implementations that inherit MyUserControl instead of UserControl, and add the markup
public partial class HistoricalLineChart : MyUserControl
{
public HistoricalLineChart(int reportID)
: base()
{
Chart1.Titles[0].Text = "Hello World";
}
}
You could also create an interface that describes any controls that should appear in the markup and implement that. This is nice because it gives you a construct that is applicable to either a UserControl (where the controls are defined in markup) or a WebControl (where the controls are created in code), leaving the actual details of the markup to each implementation, but letting you share the functionality.
Unfortunately the markup portion of BaseChart is not actually part of the BaseChart class. The markup is part of a class that gets created when you compile and it inherits from BaseChart. So HistoricalLineChart only contains what you've explicitly set in BaseChart and none of the markup. The only way I know to work around this is to use a Composite Control or Custom Server Control (vs a UserControl).It's a bit more of a pain since you have to add your child controls programmatically, but should do what you want.
Here is an example: http://msdn.microsoft.com/en-us/library/3257x3ea(v=VS.100).aspx
Basically:
Inherit from CompositeControl.
Override CreateChildControls. In this method, you can add all of your child controls (like your chart).
Optional: Override Render. Override this if you need custom markup in addition to the child controls. You can output your custom markup plus call RenderControl on all of your child controls to tell them where to render their output. If you don't override this method at all, then the composite control will render out the child controls in the order that they are in the controls collection.
Here are a couple more tutorials:
http://www.codeproject.com/KB/custom-controls/customdialog.aspx
http://msdn.microsoft.com/en-us/library/aa479016.aspx
http://www.codersource.net/asp-net/asp-net-advanced/creating-a-composite-control-in-asp-net.aspx
You can make a protected property in BaseChart that exposes the chart.

Categories