Winforms Data Binding to Custom Class - c#

I am trying to bind some Winform objects to a custom class, more specifically an instance of my custom class which I have added to the Form in the code. C#, .NET 2010 Express.
For example, here is a fragment of the class, and the UserInfoForm
public class UserInfo
{
[XmlAttribute]
public string name = "DefaultName";
[XmlAttribute]
public bool showTutorial = true;
[XmlAttribute]
public enum onCloseEvent = LastWindowClosedEvent.Exit;
}
public enum LastWindowClosedEvent
{
MainMenu,
Exit,
RunInBackground
}
public partial class Form1 : Form
{
UserInfo userToBind = new UserInfo();
TextBox TB_userName = new TextBox();
CheckBox CB_showTutorial = new CheckBox();
ComboBox DDB_onCloseEvent = new ComboBox();
public Form1()
{
InitializeComponent();
}
}
Now, I would like to bind the values of these form controls to their respective value in userToBind, but have had no luck. All the tutorials I can find are either way out of date (2002), or about binding controls to a dataset, or other type of database.
I am obviously overlooking something, but I haven't figured out what.
Thank you very much for any info you can share.
More info: UserInfo is designed to be XML-friendly so it can be saved as a user profile. UserInfo will contain other custom XML classes, all nested under the UserInfo, and many controls will only need to access these child classes.

You can use the DataBindings property of your controls (textbox, checkbox...) to add a binding to a specific control. For instance:
public Form1()
{
InitializeComponent();
TB_userName.DataBindings.Add("Text", userToBind, "name");
}
Also, IIRC, data binding only works on properties, so you'll first need to modify your UserInfo class accordingly. Moreover, if you want the UI to update automatically when modifying your objects in code, you must implement INotifyPropertyChanged in your custom classes.

Related

Winforms MVP Pattern w/ Multiple Views

I recently created a winforms application that followed no specific design pattern. This application has 4 different "views", each implemented using a TableLayoutPanel. One view is a "main" view that allows the user to select input files and the other 3 views contain DataGridViews that allow the user to work with the data loaded from the input file.
The problem lies in the fact that I have a single form with 4 different panels within it that are hidden and made visible when needed. But this has caused my form class to become much larger than I would like considering I have different events and methods that operate on the data for each panel all within the same class. So I did some research and came across Model-View-Presenter, but I've only came across examples that show applications with single-views.
My question is, if I use MVP and each view has its own interface and presenter, and the concrete implementation of the view is done using a Form, what is the best way to switch between views (for example, when clicking "next").
Should the concrete implementation of my view even be a Form? Am I missing something here? I'd like to follow the MVP pattern but I am open to suggestions if there is a better alternative.
First of all, you want to create a UserControl for each of the three DataGridView forms. As you are using MVP, each one should have an interface that the control inherits. For example:
public interface IDataGridView1
{
// Properties, Methods, etc...
}
public interface IDataGridView2
{
// Properties, Methods, etc...
}
public interface IDataGridView3
{
// Properties, Methods, etc...
}
Here is an example of the DataGridView1 UserControl, which inherits from its interface, and also from Control:
public class DataGridView1 : Control, IDataGridView1
{
TableLayoutPanel layoutPanel;
public DataGridView1()
{
layoutPanel = new TableLayoutPanel();
// Set up the layoutPanel.
// Rest of constructor, define your controls.
// Add your controls to layoutPanel.
// Add layoutPanel to this control.
Controls.Add(layoutPanel);
}
// Methods etc...
}
The other two DataGridViews will be similar but with their own functionality.
You could then create an interface for the MainView, which includes properties for the three DataGridViews it should contain, and methods to show one DataGridView whilst hiding the rest:
public interface IMainView
{
IDataGridView1 DataView1 { get; set; }
IDataGridView2 DataView2 { get; set; }
IDataGridView3 DataView3 { get; set; }
void ShowOnlyDataView1();
void ShowOnlyDataView2();
void ShowOnlyDataView3();
// Other methods, properties, etc...
}
The MainView class would inherit from Form and its own interface. Here I have shown the instantiated DataGridViews being passed in via the form's constructor:
public class MainView : Form, IMainView
{
public IDataGridView1 DataView1 { get; set; }
public IDataGridView2 DataView2 { get; set; }
public IDataGridView3 DataView3 { get; set; }
TableLayoutPanel layoutPanel;
public MainView(IDataGridView1 dataView1, IDataGridView2 dataView2,
IDataGridView3 dataView3)
{
this.DataView1 = dataView1;
this.DataView2 = dataView2;
this.DataView3 = dataView3;
layoutPanel = new TableLayoutPanel();
// Define your layout panel here.
// Add your controls to layoutPanel.
// Add layoutPanel to the MainView.
Controls.Add(layoutPanel);
// Rest of constructor...
}
// Hides other views and show DataView1.
public void ShowOnlyDataView1()
{
DataView2.Hide();
DataView3.Hide();
DataView1.Show();
}
// Hides other views and show DataView2.
public void ShowOnlyDataView2()
{
// Etc...
}
// Hides other views and show DataView3.
public void ShowOnlyDataView3()
{
// Etc...
}
// Other Methods etc...
}
Here is an example of the your Main method. You will want to instantiate each DataGridView and pass these into your MainView:
public static void Main(string[] args)
{
IDataModel dataModel = new DataModel();
IDataGridView1 dataView1 = new DataGridView1();
IDataGridView2 dataView2 = new DataGridView2();
IDataGridView3 dataView3 = new DataGridView3();
IMainView mainView = new MainView(dataView1, dataView2, dataView3);
DataGridPresenter1 dataPresenter1 = new DataGridPresenter1(dataView1, dataModel);
DataGridPresenter2 dataPresenter2 = new DataGridPresenter2(dataView2, dataModel);
DataGridPresenter3 dataPresenter3 = new DataGridPresenter3(dataView3, dataModel);
MainPresenter mainPresenter = new MainPresenter(mainView, dataModel);
}
Something to that effect.
So your three DataGridViews are displayed within your MainView, and all four views are accessed by their own Presenters.

UWP (XAML / C#) access method from parent Page within a programmatically added UserControl

I've created a UWP XAML Page where I programmatically add a UserControl and then is displayed in a ContentControl.
UserControl control = new Views.PlayingGameScreen();
this.ContentControl.Content = control;
I'm wondering how I access a method in the parent Page from the UserControl.
I think the cleanest way is to expose a property on the UserControl that the parent sets.
public partial class PlayingGameScreen : UserControl
{
public ICommand Command { get; set; }
public void SomeMethod()
{
this.Command?.Execute(null);
}
}
//parent code, define the command with the logic to run
var command = new DelegateCommand(...);
var control = new Views.PlayingGameScreen
{
Command = command
};
this.ContentControl.Content = control;
By doing it this way, the parent sets the Command and the UserControl doesn't need to know what the hosting parent is and there is no global class the UserContol uses. This is a completely decoupled solution.
Bonus, setup the Command as a DependencyProperty and then it can be assigned in xaml as well. You can bind the Command to your ViewModel and the ViewModel will be the Page's ViewModel.
<uc:PlayingGameScreen Command="{x:Bind SomeCommand}"/>
I've tried lots of different ways and this is my favorite.
From my little experience with UWP (1 app and counting :D):
You can keep a reference of your ParentPage in a static property of a class, let's name it PagesGateway.cs
The static class code:
public static class PagesGateway
{
public static ParentPage ParentPage {get; set;}
}
Then in you code behind ParentPage, you'll need to set the reference:
public partial ParentPage:Page
{
public ParentPage{
PagesGateway.ParentPage = this;
}
}
This way you'll have access to ParentPage instance methods anywhere.
But I have a feeling that's not the best practice out there. But i use this a lot to access my ViewModels from anywhere, and it work perfectly, but you have to take into account that it void the re-usability of a ViewModel

Bind custom object List to Combox (DisplayMember is not working)

I am trying to bind a List to a Combobox as datasource. My list is composed of custom class objects.
Binding works fine, but I can not set DisplayMember.
My class definitions; I have a custom class "Sett_Colection" that keep a List of another custom class
"Sett".
public class Sett
{
public string nameOfSett;
public Sett(){
...
}
}
public class Sett_Colection
{
public List<Sett> listOfSetts;
public Sett_Colection(){
...
}
}
The code in my Form is something like this;
public partial class Form1: Form
{
Sett_Colection collectionOfSetts;
public Form1()
{
// Here I add Sett instances into collectionOfSetts
// So collectionOfSetts.listOfSetts is not empty
combobox1.DataSource = collectionOfSetts.listOfSetts;
cmb_ayar.DisplayMember = "nameOfSett";
}
}
When I did this, datasource assigned succesfully. But display member has not been set as "nameOfSett".
The item names display as "Namespace.Sett";
I found a lot of example codes on internet, but none of them worked. I think my situation is a bit different
You need it to be a property:
public string nameOfSett {get; set;}

Customise the display of a auto-generated class

I have a DB and I created classes to access/manage it using SQLMetal. Now, I use LINQ to SQL, and I want to display the result of queries in a data grid view. when I do that, the columns are named after my columns in the DB table, and all the properties are displayed. I know I could change this by using DisplayName and Browseable attributes, but because the classes are auto-generated I can't just add this attributes where there are needed. I came up with three workarounds:
creating an Adopter to adopt my classes. I'm still not sure how exactly you make an adopter for this case.
creating another program that will run after the generating of the code that will add the attributes. this seems like an hack, and I prefer to separate between the functionality and the GUI, so this method is on hold.
using the MetaDataType attribute. I couldn't get this to work, and it's requires, as far as I know, that the classes and the metadata class will be in the same DLL.
How do I do the customization? is there another way? what way should I take and how?
EDIT: forgotten to mention: I'm using winforms, but if it will simplify things, I will move to WPF.
You can set a Types Metadata Type at run time by registering it with the TypeDescriptor manually.
That goes something like this.
var type = typeof(Foo);
var metadataType = typeof(FooMetadata);
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType), type);
To show it all in context this will show a single column in the data grid with the header "Custom Bar".
public class Foo
{
public string Bar { get; set; }
public string DontShowMe { get; set; }
}
public class FooMetadata
{
[DisplayName("Custom Bar")]
public string Bar { get; set; }
[Browsable(false)]
public string DontShowMe { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var type = typeof(Foo);
var metadataType = typeof(FooMetadata);
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType), type);
this.dataGridView1.DataSource = new List<Foo> { new Foo { Bar = "Foobar" } };
}
}
The is also a TypeDescriptor.RemoveProviderTransparent if you want to switch the Metadata Type on the go but keep in mind that setting/unsetting it applies to the whole application domain and so threading needs to be taken into account.
By using the WPF DataGrid you can easily customize the auto generated columns by using the AutoGeneratingColumn event.
Why can't you use the data grid view's Columns collection to change DisplayNames and Visibles at runtime?

C# Databinding: Create object if null

I have a report object (i.e. a business object) which has several dozen fields to populate. Each field by itself has INotifyPropertyChanged implemented. There is an accessor property for the active report called ActiveReport.
What I want to do is be able to Close the current report, without necessarily opening a new one, and be able to automatically create a report object when the user starts to enter data again.
Here is a rough idea of the structure. ActiveReport is the current report. The GUI is able to directly set the fields of the subclass (name/email) through binding. I want a new BusinessObject to be created when name is being set, but ActiveReport is null. One additional caveat, the report object is auto-generated from XSD files, so I'd rather not have to modify those.
class ControlClass {
public BusinessObject ActiveReport { get; set; }
}
class BusinessObject {
UserInfo field1 { get; set; }
}
class UserInfo : INotifyPropertyChanged {
DependencyProperty name;
DependencyProperty email;
}
I thought of the following scenarios:
Accessor property.
The binding does not seem to use the accessor.
Inserting a check into all event handlers.
I'd rather not have to resort to this -- this breaks the rationale behind using MVVM.
Multibinding
This would require the use of a converter class and instance, and that seems like overkill.
Converter
I thought to ask if there were any other good programming models for this in WPF.
You could create a behavior.
in it, you check if (AssociatedObject.DataContext as ReportObject) is null
and if it is, clear all your fields / set your datacontext / whatever
This should do the trick:
public class ControlClass
{
public BusinessObject ActiveReport { get; set; }
private UserInfo _editableUserData
public UserInfo EditableUserData
{
get { return _editableUserData; }
set
{
if (_editableUserData != null)
_editableUserData.PropertyChanged -= UserDataChanged;
_editableUserData = value;
if (_editableUserData != null)
_editableUserData.PropertyChanged += UserDataChanged;
RaisePropertyChanged("EditableUserData");
}
}
private void UserDataChanged(object sender, PropertyChangedEventArgs e)
{
if (ActiveReport == null)
ActiveReport = new BusinessObject(EditableUserData);
}
}

Categories