WPF binding static list to combobox - wpf

Trying to understand how to bind this static list to a combobox that located on different window.
public partial class MainWindow : Window
{
public static List<Classes.Entity> EntityList { get; set; }
public MainWindow()
{
EntityList = new List<Classes.Entity>();
InitializeComponent();
}
...
the object:
public class Entity
{
public string entityName { get; set; }
...
XAML (In a diffrent window, call "NewRelationship.xaml.cs"
<ComboBox x:Name="cb_from" ItemsSource="{Binding Path=EntityList}" DisplayMemberPath="entityName" SelectedValue="{Binding Path=Entity}" />
Of course I fill the list later in the code...
if I moving the list to the newRelationship window and add "this.datacontext = this;" its working,
How do I make this work when the list is in the mainWindow? Thanks...

A better approach would be to keep the EntityList in a separate object that both windows could reference:
class ViewModel
{
private List<Classes.Entity> _entityList = new List<Classes.Entity>();
public IEnumerable<Classes.Entity> EntityList
{
get { return _entityList; }
}
}
partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
When the second window is created, you can pass an instance of the ViewModel class to it, and set it as the DataContext.

Related

Update ObservableCollection item and related view, when the item implements an interface

As written everywhere (e.g. here, here, here, here...), reporting an ObservableCollection item property change to a view requires the item to implement INotifyPropertyChanged.
Using CommunityToolkit.Mvvm this can be done using an attribute:
public class MyViewModel : ObservableObject
{
public ObservableCollection<MyItem> MyCollection { get; set; }
//...
}
[INotifyPropertyChanged] // yay! No boilerplate code needed
public partial class MyItem
{
public string MyProperty { get; set; }
}
If somewhere inside MyViewModel there is a change to the MyProperty of an item of MyCollection, the view will be updated.
What if an interface comes into play?
public class MyViewModel : ObservableObject
{
public ObservableCollection<IMyInterface> MyCollection { get; set; }
//...
}
[INotifyPropertyChanged]
public partial class MyItem : IMyInterface // MyProperty is in IMyInterface too
{
public string MyProperty { get; set; }
}
The view seems not to be updated anymore.
I tried:
Inheriting INotifyPropertyChanged in IMyInterface (that requires explicit implementation of the PropertyChanged event and OnPropertyMethod method in MyItem, which I don't want as otherwise I would have not used CommunityToolkit.Mvvm)
Adding [INotifyPropertyChanged] to MyViewModel (expecting nothing but there was an answer somewhere telling that)
Is there an obvious, no-boilerplate solution that I'm missing here?
The view is updated if I do something like suggested here
var item = MyCollection[0];
item.MyProperty = "new value";
MyCollection[0] = item;
but I hope there's a better way.
I'm not sure this will solve your issue but I can see you're using the attribute incorrectly.
Please see
https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/inotifypropertychanged
"These attributes are only meant to be used in cases where the target types cannot just inherit from the equivalent types (eg. from ObservableObject). If that is possible, inheriting is the recommended approach, as it will reduce the binary size by avoiding creating duplicated code into the final assembly."
You're not inheriting from IMyInterface, you're implementing it.
Your viewmodel should inherit ObservableObject
Instead of
[INotifyPropertyChanged]
public partial class MyItem : IMyInterface
You should have:
public partial class MyItem : ObservableObject, IMyInterface
Properties look like:
[ObservableProperty]
private List<FlatTransactionVM> transactions = new List<FlatTransactionVM>();
That generates a public Transactions property.
If you only have properties, you don't need that to be a partial class. That's necessary for relaycommand generation.
EDIT
This works for me:
MainWindow
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding MyCollection}"
DisplayMemberPath="MyProperty"/>
</Grid>
MainWindowViewModel
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<IMyInterface> myCollection = new ObservableCollection<IMyInterface>();
public MainWindowViewModel()
{
MyCollection = new ObservableCollection<IMyInterface>(
new List<MyItem>{
new MyItem{ MyProperty = "A" },
new MyItem { MyProperty = "B" },
new MyItem { MyProperty = "C" }
});
}
}
MyItem
public partial class MyItem : ObservableObject, IMyInterface
{
[ObservableProperty]
private string myProperty;
}
Interface
public interface IMyInterface
{
string MyProperty { get; set; }
}
Quick and dirty code in the view. This is purely to see what happens when one of the MyItems.MyProperty is set.
private async void Window_ContentRendered(object sender, EventArgs e)
{
await Task.Delay(5000);
var mw = this.DataContext as MainWindowViewModel;
mw.MyCollection[1].MyProperty = "XXXXX";
}
After a short wait, I see the second item change to XXXXX as expected.

Simple Windows Phone User Control Databinding Does Not Work

I have an issue with something that should be very simple databinding scenario. I want to bind a list of items. I want to create a user control put it in a ItemsControl's template and bind the ItemsControl to some data. I am perfectly happy with one time databinding so I was kind of hoping to avoid learning about dependency properties and all the databinding stuff for this simple scenario.
Here is the XAML for the user control:
<TextBlock>Just Something</TextBlock>
And the code behind:
namespace TestWindowsPhoneApplication
{
public partial class TestControl : UserControl
{
public TestData SomeProperty { get; set; }
public String SomeStringProperty { get; set; }
public TestControl()
{
InitializeComponent();
}
}
}
MainPage.xaml:
<ItemsControl Name="itemsList" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<t:TestControl SomeStringProperty="{Binding Path=SomeString}"></t:TestControl>
<!--<TextBlock Text="{Binding Path=SomeString}"></TextBlock>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is MainPage.xaml.cs:
namespace TestWindowsPhoneApplication
{
public class TestData
{
public string SomeString { get; set; }
}
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
itemsList.DataContext = new TestData[] { new TestData { SomeString = "Test1" }, new TestData { SomeString = "Test2" } };
}
}
}
When I run the project I get an error "the parameter is incorrect". I also tried binding directly to the item with SomeProperty={Binding} since that is what I actually want to do but this causes the same error. If I try doing the same thing with the TextBlock control (the commented line) everything works fine.
How can I implement this simple scenario?
To make a property on your custom control "bindable" you have to make it a dependency property. Check out my answer here for a nice simple example of doing just this on a custom control: passing a gridview selected item value to a different ViewModel of different Usercontrol
public string SomeString
{
get { return (string)GetValue(SomeStringProperty); }
set { SetValue(SomeStringProperty, value); }
}
public static readonly DependencyProperty SomeStringProperty =
DependencyProperty.Register("SomeString", typeof(string), typeof(TestControl),
new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnSomeStringChanged)));
private static void OnSomeStringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TestControl)d).OnSomeStringChanged(e);
}
protected virtual void OnSomeStringChanged(DependencyPropertyChangedEventArgs e)
{
//here you can do whatever you'd like with the updated value of SomeString
string updatedSomeStringValue = e.NewValue;
}

Why won't MEF Lazy load work in the ViewModel?

I'm trying to get Lazy to work for a collection in my ViewModel that I'm binding to. The collection loads through MEF fine, but never gets displayed in the bound UI.
Here's the UI:
<Window x:Class="TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<ItemsControl ItemsSource="{Binding MyList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding ItemTitle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel>
</Window>
The code-behind class:
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent();
this.DataContext = new TestVM();
}
}
The ViewModel:
public class TestVM : INotifyPropertyChanged, IPartImportsSatisfiedNotification
{
public TestVM()
{
//I'm using a static class to initiate the import
CompositionInitializer.SatisfyImports(this);
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
[ImportMany(typeof(MyItemBase))]
public Lazy<MyItemBase>[] MyList { get; set; }
public void OnImportsSatisfied()
{
this.PropertyChanged(this, new PropertyChangedEventArgs("MyList"));
}
}
The base class for the items, and some inherited test classes:
[InheritedExport]
public class MyItemBase
{
public MyItemBase()
{
}
public string ItemTitle{ get; set; }
}
public class MyItem1: MyItemBase
{
public MyItem1()
{
this.ItemTitle = "Item 1";
}
}
public class MyItem2: MyItemBase
{
public MyItem2()
{
this.ItemTitle = "Item 2";
}
}
This works IF I just remove the Lazy loading. However, I'll need to apply some export attributes later, which means going to Lazy.
the problem is that you want bind to a list of MyItembase object, but your actual binding is to a lazy arrray of MyItembase objects.(as long as you never call .Value for your lazy item nothing will happen)
i my projects i use a private lazy collection for mef and a normal ObservableCollection for wpf. btw i would prefer Constructor injection for your Mef import
public class TestVM : INotifyPropertyChanged, IPartImportsSatisfiedNotification
{
public TestVM()
{
//I'm using a static class to initiate the import
CompositionInitializer.SatisfyImports(this);
this.MyList = new ObservableCollection();
foreach(var lazyitem in _mefList)
{
this.MyList.Add(lazyitem.Value);
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public ObservbableCollection<MyItemBase> MyList{ get; set; }
[ImportMany(typeof(MyItemBase))]
private IEnumarable<Lazy<MyItemBase>> _mefList { get; set; }
public void OnImportsSatisfied()
{
//this.PropertyChanged(this, new PropertyChangedEventArgs("MyList"));
}
}

WPF data binding issue

I am just new to WPF.
I have a wpf app and there i simply have a dock panel and inside dock panel i have a textblock.
I want to bind the text property of textblock to my custom object's property but that' not working.
I think i am missing something here but don't know what.
Here is the code snippet.
<TextBlock Text="{Binding Source=myDataSource, Path=ColorName}"/>
</DockPanel>
My custom class.
class MyData
{
public string ColorName { get; set; }
}
and main window constructor..
public partial class MainWindow : Window
{
MyData myDataSource;
public MainWindow()
{
InitializeComponent();
myDataSource = new MyData { ColorName = "Red" };
}
}
myDataSource needs a get and set. You also need to set the dataContext for the window, so it should be-
public partial class MainWindow : Window
{
public MyData MyDataSource { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
MyDataSource = new MyData { ColorName = "Red" };
}
}
public class MyData
{
public string ColorName { get; set; }
}
and xaml code should be -
<TextBlock Text="{Binding MyDataSource.ColorName}"/>
edit Sorry got this wrong I've changed to the correct code
What you bind to needs to be a
public property. (Your data-object needs to satisfy that condition as well)
Unless you set the property before
InitializeComponent() the binding
will might not update depending on your binding.
If you set it again at any point in
time after the initilization and want the binding to be updated you
should implement
INotifyPropertyChanged or work
with dependency properties.
Since the data is in your window you might need to access it over that:
{Binding ElementName=window, Path=myDataSource.ColorName}
If you only want to bind to MyData, don't set window as its own DataContext. Istead, set the data you're binding to. This way it's more clear what is data, and what is view.
Also, lose the Source on Binding, you won't generally need it.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyData { ColorName = "Red" };
}
}
public class MyData
{
public string ColorName { get; set; }
}
XAML:
<TextBlock Text="{Binding ColorName}"/>

Wpf usercontrol's Button not firing the ICommand in Presenter

I am new to WPF and tring to learn a WPF composite application.
I have got user control which is a module which 1 label and 1 button on it.
Now I am trying to use the Command property on button which looks like
<Button Name="button1" Command="{Binding Path = GetCustomerNameCommand}">GetCustomer</Button>
In the presenter I have got
public class HelloWorldPresenter : PresenterBase<IHelloWorldView>
{
private readonly IWpfService _wpfService;
public HelloWorldViewModel CustomerViewModel { get; set; }
public ICommand GetCustomerNameCommand { get; set; }
public HelloWorldPresenter(IHelloWorldView view, IWpfService wpfService)
: base(view)
{
_wpfService = wpfService;
GetCustomerNameCommand = new DelegateCommand<string>(GetCustomerName);
View.Presenter = this;
PopulateViewModel(string.Empty);
}
private void GetCustomerName(string obj)
{
throw new NotImplementedException();
}
private void PopulateViewModel(string test)
{
CustomerViewModel = new HelloWorldViewModel { Name = _wpfService.GetCustomer() };
}
}
The thing is GetCustomerName() method is not getting executed when i click the Button
I found it, i was adding the same view 2 times which was creating the problem...

Resources