How to Passing multiple parameters RelayCommand? [duplicate] - wpf

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Passing two command parameters using a WPF binding
I need that send two parameters to my RelayCommand like:
public RelayCommand<String,Int> MyCommand {get;set;} Or
public RelayCommand<EventArgument,String> MyCommand {get;set;}

Wrap them in an object:
public RelayCommand<MyModel> MyCommand { get; set; }
where MyModel will contain the two properties:
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
}

You can use a distinct model class in order to pass several parameters. And in order to initialize them, you can use xaml elements like this:
<Button Command="{Binding YourCommand}">
<Button.CommandParameter>
<YourNS:YourModel Id="{Binding PathForId}" Name="{Binding PathForName}"/>
</Button.CommandParameter>
</Button>
This will construct a new YourModel object to pass to a command, and then will initialize its properties via bindings.

Related

Binding to a property whose value is an object [duplicate]

This question already has answers here:
Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.String'
(2 answers)
Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.Nullable`1[System.Guid]'
(1 answer)
Closed 6 months ago.
I am using Visual Studio 2022 Community Edition.
I have the following structure in my app
An class holding some data
class MyData {
public string Property1 { get; set; }
public int Property2 { get; set; }
}
A user control that has a property of type MyData
class MyControl: UserControl {
public MyData Data { get; set; } = new MyData();
// more code here
}
A window where I want to be able to include the user control, to display its MyData type property.
Code behind is:
class MainWindow: Window {
public MyData Data { get; set; } = new MyData();
// more code here
The MainWindow XAML contains:
<MyControl Data="{Binding Data}" />
The designer puts a squiggly line under the Data attribute in the XAML and in the Errors window it gives me the following error:
XDG0062 Object of type 'System.Windows.Data.Binding' cannot be converted to type 'MyData'.
Building the project succeeds without any errors.
What am I doing wrong? How can I bind the MainWindow's property to the MyControl's property?

DataBinding issue - DataTemplate/ViewModel/ICollectionView

Initially I posted this to the PRISM4 forum but got a suggestion that I should
try this forum as well:) I'm using WPF4 BTW...
I'm running PRISM4 and I've been struggling to get my data binding to work.
I'm following the MVVM pattern and have a view model which initially loads
data from a RDBMS and wraps it in an ICollectionView.This works perfectly, the data is displayed in the bound DatGrid, but I'm struggling in my eforts when trying to persist
changes made to the data which is presented in a DataGrid declared below.
The view model publishes the ICollectionView through a read/write property,
"Results", which, as you can see has a binding mode of "TwoWay". I thought
this would be enough to persist the changes made to the state of the
checkboxes but no:( I've experimented with a number of ways to accomplish
this but the state of the checkbox is not propagated back to the view model.
I've intercepted the call to the "PlotClicked" method which is an ICommand
object but the argument being passed has an unchanged "Plot" attribute!
This is especially obvious when I click one of the column headers and the
view is sorted - the checked rows are unchecked which is the default state of the checkboxes when retrieved from the db.
What am I doing wrong here?
Many thanks in advance - I'm really stuck here:(
/Peter
<DataGrid Grid.Row="0" Name="gridResults" ItemsSource="{Binding Results,Mode=TwoWay}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Plot">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=Plot, Mode=TwoWay}"
HorizontalAlignment="Center"
Command="{Binding Path=DataContext.PlotClicked,Mode=OneWay, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...
I tried out the suggestions pointed out to me. This is what I've done:
In the view-model I changed the Result property from ICollectionView to OC
public ObservableCollection Results { get; set; }
Added the following template resources for the UserControl making up the View
Added the following code to the DataGrid in the "Columns" section
<DataGridTemplateColumn
Header="cbTest"
x:Name="cbTest"
CellTemplate="{StaticResource IsSelectedColumnTemplate}"
CellEditingTemplate="{StaticResource IsSelectedColumnTemplateEditing}"
CanUserSort="True"
Width="Auto"
/>
After having made those changes I experimented with various settings for the UpdateSourceTrigger in the IsChecked="{Binding mentioned... in (2) above with no effect. The changes I make to the checkboxes are not transferred back to the view-model's ObservableCollection.
Again, many thanks for trying to help me out here!
* UPDATE *
Now I've experienced something REALLY SPOOOOKY:( This is what I've done:
public class ResultViewResult : IReslutViewResult
{
public bool Plot { get; set; }
public Guid ResultId { get; set; }
public DateTime Generated { get; set; }
public int Duration { get; set; }
...
This didn't work in a sense that the 'Plot property' could NEVER be set to true by clicking the checkbox column in the DataGrid! Now I did the following:
public class ResultViewResult : IReslutViewResult
{
private bool _plot;
public bool Plot
{
get
{
return _plot;
}
set
{
_plot = value;
}
}
public Guid ResultId { get; set; }
public DateTime Generated { get; set; }
public Guid ResultId { get; set; }
public DateTime Generated { get; set; }
public int Duration { get; set; }
...
The result you may ask? It works and the 'Plot' is correctly set! Now, I thought, this is weird!!! So what I did was the following (simply commenting out the private var and get/set code):
public class ResultViewResult : IReslutViewResult
{
public bool Plot { get; set; }
//private bool _plot = false;
//public bool Plot
//{
// get
// {
// return _plot;
// }
// set
// {
// _plot = value;
// }
//}
public Guid ResultId { get; set; }
public DateTime Generated { get; set; }
public int Duration { get; set; }
...
Ok, what about the result? IT WORKS!!!??? I'm stunned... I mean what's the difference between the first and the last???? I feel VERY awkward about this - I mean I want to know WHAT'S going on behind the scene here... :(
Not sure about that, but I'd suggest you to try with an ObservableCollection used as ItemsSource . I had a lot of problems like that before, all of them were solved using this kind of collection (which is btw faster and less consuming than a classic collection for refreshing purposes).
Also, try to add in the IsChecked binding the following: UpdateSourceTrigger=PropertyChanged, this could do the trick, I think the only problem here is that the source isn't updated at the right time!
Not sure if this is the issue in your case, but the DataGrid uses a variation of databinding that will not commit the changes to the source until you move off the row. This is called a BindingGroup. Maybe you're not seeing the values committed because you haven't moved off the row yet?
http://blogs.msdn.com/b/vinsibal/archive/2008/08/11/wpf-3-5-sp1-feature-bindinggroups-with-item-level-validation.aspx
One other possibility is that the binding path is somehow not correct? Have you checked the output window in VS to see if it's reporting any binding path failures?

HierarchicalDataTemplate Link By Parent

I want bind my treeview. There are a lot of samples binding treeview by object, which contains children collection. I've got domain having just Parent pointer.
public class Service : BaseDomain
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual Service Parent { get; set; }
}
Can I bind collection of this objects to my treeView. Thanks
It's not possible with HierarchicalDataTemplate, but you could create custom convertedr for ItemsSource binding. In general case converting such structure to tree might be resource intensive task (particularly it requires that treeview grabs all your data before displaying tree root).

Declaring list and binding property values via XAML for a custom silverlight control

I feel like I've missed something obvious, but when creating a custom control in Silverlight I can declare properties as public and they will be exposed when making XAML declarations, so I can easily do something like this:
public class MyControl : UserControl
{
public string Title {get; set;}
}
And this:
<local:MyControl Title="Hello World" />
However properties are not always simple types, I may have a complex property, like a list that defines 1 or more columns and identifies a field to be bound to it. With existing controls the syntax for defining these values in XAML is straight forward:
<local:MyControl Title="People List">
<local:MyControl.Columns>
<local:MyControlColumn Heading="Column 1" Binding="{Binding Name}" />
<local:MyControlColumn Heading="Column 2" Binding="{Binding Age}" />
</local:MyControl.Columns>
</local:MyControl>
However I'm at a loss of how to make this work in the class definition:
public class MyControl : UserControl
{
public string Title {get; set;}
public IEnumerable ItemSource {get; set;}
public ObservableCollection<MyControlColumn> Columns {get; set;} // ?
}
public class MyControlColumn
{
public string Heading {get; set;}
public ??? Binding {get; set;} // ?
}
Can anyone point me in the right direction for making exposed list and binding properties?
For the Columns collection create a backing private variable to hold an instance collection and remove the set accessor:-
private ObservableCollection<MyControlColumn> _columns = new ObservableCollection<MyControlColumn>();
public ObservableCollection<MyControlColumn> Columns {get { return _columns; } }
For the Binding property it looks like what you might want is the Binding type itself System.Windows.Data.Binding. However I think I'd need to know a little more about what you intend to do with the object once you've got it.
You should consider making the MyControl Title property a DependencyProperty:-
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(MyControl), null);
Similarly for the ItemSource although you should also rename to ItemsSource to keep convention with existing controls.
I can't help feel there is some re-invention of the wheel here, does DataGrid or ListBox or some other similar control not cut it for you?

Using a BindingSource in a UserControl

I have a UserControl with multiple fields that I would like to have bound to a BindingSource. I would also like the UserControl to expose some BindingSource property so that it can be dropped on a Form and be bound to the BindingSource on the form. Is there an easy way to do this? I realize that I can rebind all of the controls of the UserControl in its BindSource setter. But this seems wrong. Is there some BindingSource Proxy that will let me link the BindingSource in the user control to the BindingSource in the form?
As per your question, I can hardly get what you intend to do. Thus I will try my best to provide you with, I hope, interesting information on that matter.
First, let's consider the following UserControl in a Customer management software project.
public partial class CustomerManagementUserControl : UserControl {
public CustomerManagementUserControl() {
InitializeComponent();
_customerBindingSource = new BindingSource();
}
public IList<ICustomer> DataSource {
set {
_customerBindingSource.DataSource = value;
}
}
private BindingSource _customerBindingSource;
}
Second, let's consider the following Form which should be your Customer management form.
public partial class CustomerManagementForm : Form {
public CustomerManagementForm() {
InitializeComponent();
_customerUserControl = new CustomerManagementUserControl();
_customerUserControl.Name = #"customerUserControl";
}
private void CustomerManagementForm_Load(object sender, EventArgs e) {
// CustomersFacade is simply a static class providing customer management features and requirements.
// Indeed, the GetCustomers() method shall return an IList<ICustomer>.
// The IList type and typed IList<T> are both intended to be bindable as a DataSource for DataBinding.
_customerUserControl.DataSource = CustomersFacade.GetCustomers();
this.Controls.Add(_customerUserControl);
}
private CustomerManagementUserControl _customerUserControl;
}
If you're expecting to use CustomerManagementUserControl.DataSource property from within the Property window, please consider adding the following on top of your property definition.
[System.ComponentModel.DesignTimeVisible(true), System.ComponentModel.DesignerCategory("CustomerUserControl"), System.ComponentModel.Description("Sets the CustomerUserControl DataSource property")]
This is one way of doing what I guess you might want to do. On the other hand, if what you wish to do is to get the as most abstract as possible by setting a different type of object as your UserControl.BindingSource.DataSource property, then you will have to write a method which could detect the type of the object passed, then binding the properties accordingly. A nice way you could go, perhaps, is by Reflection, if you're comfortable working with it. In any possible way you may imagine working with such polymorphism features, you will have to write yourself an interface that all of your bindable objects will have to implement. This way, you will avoid unknown property names, and when will come the time to bind your UserControl's controls, you will be able to bind the correct property to the correct control and so forth.
Let's try the following:
public interface IEntity {
double Id { get; set; }
string Number { get; set; }
string Firstname { get; set; }
string Surname { get; set; }
long PhoneNumber { get; set; }
}
public interface ICustomer : IEntity {
}
public interface ISupplier : IEntity {
string Term { get; set; }
}
public sealed Customer : ICustomer {
public Customer() {
}
public double Id { get; set; }
public string Number { get; set; }
public string Firstname { get; set; }
public string Surname { get; set; }
public long PhoneNumber { get; set; }
}
public sealed Supplier : ISupplier {
public Supplier() {
}
public double Id { get; set; }
public string Number { get; set; }
public string Firstname { get; set; }
public string Surname { get; set; }
public long PhoneNumber { get; set; }
public string Term { get; set; }
}
Considering the above code, you could use the DataSource property of your UserControl to bind with an IEntity, so your property could like like this.
[System.ComponentModel.DesignTimeVisible(true), System.ComponentModel.DesignerCategory("CustomerUserControl"), System.ComponentModel.Description("Sets the CustomerUserControl DataSource property")]
public IList<IEntity> DataSource {
set {
_customerBindingSource.DataSource = value;
}
}
That said, if you wish to push even further, you could just expose your UserControl's controls DataBindings properties in order to set them on design-time. Considering this, you will want to expose your BindingSource as a public property either so that you may set it on design-time too, then choose your DataMember from this BindinSource.
I hope this helps you both a little or at least, give you some tracks for further searchings.
I know it's a late answer; however, it might be useful to someone else reading this post.
I have controls on a UserControl that are data-bound. I need to have a BindingSource on the UserControl in order to be able to bind the controls at design time. The "real" BindingSource, however, sits on the Form. In other words, the controls on the UserControl should behave as if they were sitting directly on the form (or on a ContainerControl on the form).
The idea behind this solution is to watch for the DataSourceChanged event of the "real" BindingSource and to assign its DataSource to the local BindingSource when it changes. In order to find the "real" BindingSource I let the Form (or Control) containing it implement the following interface:
public interface IDataBound
{
BindingSource BindingSource { get; }
}
We can watch for the ParentChanged event of a control in order to know when it has been added to a Form or a ContainerControl. The problem here is that this ContainerControl itself might not have been added to the Form (or another ContainerControl) yet at this time. In this case we subscribe to the ParentChanged event of the last parent we find in the parents chain and wait until this last parent has been added, an so on, until we find a Control or Form implementing IDataBound. When a IDataBound has been found, we subscribe to the DataSourceChanged event of its BindingSource.
public partial class MyUserControl : UserControl
{
private IDataBound _dataBoundControl;
private Control _parent;
public MyUserControl()
{
InitializeComponent();
if (LicenseManager.UsageMode == LicenseUsageMode.Runtime) {
_parent = this;
SearchBindingSource();
}
}
private void SearchBindingSource()
{
if (_parent != null && _dataBoundControl == null) {
while (_parent.Parent != null) {
_parent = _parent.Parent;
_dataBoundControl = _parent as IDataBound;
if (_dataBoundControl != null) {
if (_dataBoundControl.BindingSource != null) {
_dataBoundControl.BindingSource.DataSourceChanged +=
new EventHandler(DataBoundControl_DataSourceChanged);
}
return;
}
}
// This control or one of its parents has not yet been added to a
// container. Watch for its ParentChanged event.
_parent.ParentChanged += new EventHandler(Parent_ParentChanged);
}
}
void Parent_ParentChanged(object sender, EventArgs e)
{
SearchBindingSource();
}
void DataBoundControl_DataSourceChanged(object sender, EventArgs e)
{
localBindingSource.DataSource = _dataBoundControl.BindingSource.DataSource;
}
}
If you wanted to do this all automatically you could look for the binding source from the parent form in the load event of your user control or something like that...
Dim components As Reflection.FieldInfo = typ.GetField("components", Reflection.BindingFlags.DeclaredOnly Or Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
Dim lstBindingSources As New List(Of BindingSource)
For Each obj As Object In components.Components
Dim bindSource As BindingSource = TryCast(obj, BindingSource)
If bindSource IsNot Nothing Then
lstBindingSources.Add(bindSource)
End If
Next
If lstBindingSources.Count = 1 Then
MyBindingSource.DataSource = lstBindingSources(0).DataSource
End If
If you assign the same object reference as the datasource on two bindingsources, the controls will not be updated consistently on the second bindingsource. Possibly, a compromise to the choices above is the following:
Temporarily add a bindingsource to the usercontrol and use the VS designer to set the bindings to the controls.
bring the designer.vb up in the code editor. Search for all the "DataBindings.Add" lines that were created by the designer. Copy them all to notepad.
delete the bindingsource from the designer and add a bindingsource reference in code. Add a property for the bindingsource with the same name as was used in the designer. In the setter for the property, paste all the lines from notepad above in step 2.
In the Load event of the form, assign the bindingsource of the form to the property on the user control. If the user control is embedded in another user control, you can use the handlecreated event of the parent control to do the same.
There is less typing and less typos because the VS designer is creating all those literal text property names.

Resources