Object Binding in Caliburn.Micro MVVM Framework with SelectedItem - c#

I have the following Models:
public class Profile
{
public string name { get; set; }
public Member casemanager { get; set; }
public Member assistant { get; set; }
}
public class Member
{
public int Id { get; set; }
public int Type{ get; set; }
public string Name { get; set; }
}
In my ViewModel, I have the following objects:
an ObservableCollection<Profile> named Profiles that gets populated from a database;
a SelectedProfile of type Profile, of course;
Two Lists of Members named ListCaseManagers and ListAssistants that get populated from DB.
Each of the objects implement a NotifyOfPropertyChange method from Caliburn.Micro and are set with property and backing field.
The View:
<StackPanel>
<ListView ItemsSource="{Binding Profiles}" SelectedItem="{Binding SelectedProfile}" DisplayMemberPath="name" SelectionMode="Single" />
<ComboBox ItemsSource="{Binding ListCaseManagers}" SelectedItem="{Binding SelectedProfile.casemanager }" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
<ComboBox ItemsSource="{Binding ListAssistants}" SelectedItem="{Binding SelectedProfile.assistant}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
</StackPanel>
In this XAML I was thinking that the SelectedItem of each ComboBox would bind directly to the SelectedProfile casemanager/assistant, changing everytime I modify the SelectedProfile using the ListView, but it doesn't seem to select the item in the combobox even though SelectedProfile.casemanager and SelectedProfile.assistant are not null.
What am I missing? Is there an easy way using conventions with Caliburn.Micro?

In combobox present couple properties which can allow you to modify your SelectedItem in your list.
SelectedValuePath="Id" <br/>
SelectedValue="{Binding SelectedProfile.casemanager.Id}"
Upd: this approach allow to bind selected element by their some unique attribute (ID or Name)
In case if you need to work with SelectedItem.. you must sure that props of items in Profiles collection are related to object in ListCaseManagers and ListAssistants
var query = from p in Profiles
join mgr in ListCaseManagers on p.casemanager==mgr
select p;
? query.Count
enter code here
I assume you will see 0.. since objects in collection are different.

to use the convention name of Caliburn, you have to give a name to your control
for example for ListView:
<StackPanel>
<ListView x:Name ="Profiles" />
</StackPanel>
with the convention of names, Caliburn binds automatically the collection Profiles and use SelectedProfiles as SelectedItem

You either need to raise the PropertyChanged for the SelectedProfile property when it's set:
private Profile_selectedProfile;
public ProfileSelectedProfile
{
get { return _selectedProfile; }
set
{
_selectedProfile = value;
NotifyOfPropertyChange(() => SelectedProfile);
}
}
Or you could bind to the SelectedItem property of the ListView control itself:
<StackPanel>
<ListView x:Name="lv" ItemsSource="{Binding Profiles}"
SelectedItem="{Binding SelectedProfile}"
DisplayMemberPath="name" SelectionMode="Single" />
<ComboBox ItemsSource="{Binding ListCaseManagers}"
SelectedItem="{Binding SelectedItem.casemanager, ElementName=lv }"
DisplayMemberPath="Name"/>
<ComboBox ItemsSource="{Binding ListAssistants}"
SelectedItem="{Binding SelectedItem.assistant, ElementName=lv}"
DisplayMemberPath="Name"/>
</StackPanel>

Related

ComboBox binding with DisplayMemberPath and SelectedValuePath does not work as expected

I bind an ObservableCollection to a ComboBox and set the DisplayMemberPath to the Name,
with the help of SelectedValuePath I wanted to achieve that Device.ObjectId gets the value of Object.Id.
But this doesn't seem to work, the Object.Name is still set for Device.ObjectId.
How do I get the ComboBox to show the Name but set the Id?
public class Object
{
public string Id { get; set; }
public string Name { get; set; }
}
ObservableCollection Objects = new ObservableCollection<Object>();
<ComboBox ItemsSource="{Binding Objects}" DisplayMemberPath="Name" SelectedValuePath="Id">
<ComboBox.Text>
<Binding Path="Device.ObjectId" UpdateSourceTrigger="PropertyChanged">
</ComboBox.Text>
</ComboBox>
Since you set the SelectedValuePath attribute to Id, you can bind to the SelectedValue attribute to get the value. Something like this:
<ComboBox ItemsSource="{Binding Objects}" DisplayMemberPath="Name"
SelectedValuePath="Id" SelectedValue="{Binding Device.ObjectId}" />

Add static item to RadComboBox in WPF

I have a set of two cascading RadComboBoxes - when one is set, the other populates. The second combobox has its ItemSource set to a CompositeCollection that's binding to an ObservableCollection in the viewmodel.
I'm attempting to add a static value to the list. The idea is that the CompositeCollection can change, but there should always be one static ComboBoxItem available named Other.
CustomerContact.cs:
public class CustomerContact
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
CustomerContactSource:
<CollectionViewSource x:Key="CustomerContactSource" Source="{Binding CustomerSite.CustomerContacts}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
OtherCustomerContact:
public CustomerContact OtherCustomerContactItem => new CustomerContact
{
Name = "Other",
Email = string.Empty,
PhoneNumber = string.Empty
};
Xaml page:
<telerik:RadComboBox ItemTemplate="{StaticResource ComboBoxItemTemplate}"
SelectedItem="{Binding CustomerContact}"
Text="{Binding Source=CustomerContact, Path=Name}">
<telerik:RadComboBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource CustomerContactSource}}"/>
<TextBlock Text="{Binding Source=OtherCustomerContactItem, Path=Name}"/>
</CompositeCollection>
</telerik:RadComboBox.ItemsSource>
</telerik:RadComboBox>
I keep getting an error stating that no converter can be found for TextBlock to CustomerContact. What am I doing wrong? The RadComboBox has an ItemSource that's a list of CustomerContact and a single, unchanging item that's also of type CustomerContact.
Any help would be greatly appreciated!!!
The RadComboBox has an ItemSource that's a list of CustomerContact and
a single, unchanging item that's also of type CustomerContact.
That last part doesn't hold. You're wrapping the item in a TextBlock and that's
the type XAML sees. Giving an error due to lack of TypeConverter.
You could wrap the item:
in a Collection(Container) of its own.
in a RadComboBoxItem in the lines of what is
done here for a ListBox.

How to Populate an ObservableCollection through bindings

Hello I'm trying to Populate an Observable Collection through selected items of an Autocompletebox.
<Telerik:RadAutoCompleteBox Itemssource="{Binding People}" />
How should I initiate the Observable Collection to bind with RadAutocompleteBox.
And how should I set up SelectedItems binding method.
public class People
{
public string Person
{ get; set; }
public DateTime Date
{ get; set; }
}
public ObservableCollection<string> Persons{ get; set; }
You could try this to get the SelectedItems :
Add this to your viewmodel and edit your XAML.
ViewModel:
public ObservableCollection<People> SelectedPeoples { get; set; }
Xaml:
<Telerik:RadAutoCompleteBox Itemssource="{Binding People}"
SelectedItems="{Binding SelectedPeoples , Mode=TwoWay}"
SelectionMode="Multiple" />
After your edit
I see that you try to bind a class to the RadAutoCompleteBox rather than the ObservableCollection People, you have to bind the property Persons to the RadAutocompleteBox. This will bind the ObservableCollection to the RadAutoCompleteBox.
<Telerik:RadAutoCompleteBox Itemssource="{Binding Persons}" />
Also, you can edit the declaration of your ObservableCollection to
public ObservableCollection<People> Persons{ get; set; }
and edit the XAML like this:
<Telerik:RadAutoCompleteBox Itemssource="{Binding Persons}" DataMemberPath="Person" />
Your RadAutocompleteBox will now show the string Person declared in your People class.
If I'm looking at your code right, you are binding the items source to the class rather than to the collection. For an item's source binding to the collection is needed. You set the data context to the class.
Itemssource="{Binding Persons}"

Having trouble binding ViewModel to ComboBox

I have a viewmodel setup as the following
public class cDriveListVM
{
public string Drive { get; set; }
public cDriveListVM(string name)
{
Drive = name;
}
}
I declare the observablecollection in the window and set its datacontext to this observable collection.
public ObservableCollection<cDriveListVM> DriveList { get; set; }
private void dl()
{
DriveList = new ObservableCollection<cDriveListVM>();
DriveList.Add(new cDriveListVM("drive 1"));
DriveList.Add(new cDriveListVM("drive 2"));
this.DataContext = DriveList;
}
Xml for combobox:
<ComboBox x:Name="Drive_ComboBox" ItemsSource="{Binding Path=Drive}" HorizontalAlignment="Center" IsReadOnly="True" Grid.Column="0" Grid.Row="0" Width="300" Margin="10" SelectionChanged="Drive_Changed" Height="22" VerticalAlignment="Top"/>
I am just learning how to use Viewmodel so I am unsure what I am doing wrong, any help would be appreciated. I updated the xml file it results in the following combbox.
There are a few problems with this code.
One, the binding is set up wrong. Since the property with the viewmodel collection is DriveList, the binding should be ItemsSource="{Binding Path=DriveList}".
Two, you are attempting to display a field from your viewmodel, which is not doable. WPF's binding engine only works with properties, so the viewmodel should have a property:
public string Drive { get; set; }
And finally, the DisplayMemberPath should match the property name from the viewmodel: DisplayMemberPath="Drive".
Update: I just noticed that the DataContext is the observable collection itself -- I probably missed it on the first read. In that case, you want to bind directly to the data context:
ItemsSource="{Binding}"
And set DisplayMemberPath to the property you want to display:
DisplayMemberPath="Drive"

How to choose which ObservableCollection to bind to a listbox?

Is that even possible? I have two ObservableCollections. I want to bind and populate a listbox with one of them. For example let's say that we have 2 buttons - one for Twitter and one for Facebook. Clicking on a Facebook button it will populate listbox with friend's names from facebook observable collection and it will bind it. Clicking on Twitter it will populate listbox with Twitter followers and populate listbox and bind it.
How to choose which collection will be populated in listbox?
I would just use one observable collection and fill based on the users choice. You could also fill it with the names from both sources and have a filter to filter out one or the other (apparently you need a wrapper object where you can indicate whether the name is a facebook friend or twitter follower).
Edit: Here is some quick code example of how you can do it:
public interface ISocialContact
{
string Name { get; }
}
public class FacebookContact : ISocialContact
{
public string Name { get; set; }
public string FacebookPage { get; set; }
}
public class TwitterContact : ISocialContact
{
public string Name { get; set; }
public string TwitterAccount { get; set; }
}
Then in your data context:
public ObservableCollection<ISocialContact> Contacts { get; set; }
...
Contacts = new ObservableCollection<ISocialContact> {
new FacebookContact { Name = "Face", FacebookPage = "book" },
new TwitterContact { Name = "Twit", TwitterAccount = "ter" }
};
And in your xaml:
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type local:FacebookContact}">
<TextBlock Text="{Binding FacebookPage}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:TwitterContact}">
<TextBlock Text="{Binding TwitterAccount}"/>
</DataTemplate>
</Grid.Resources>
<ListBox ItemsSource="{Binding Contacts}" Width="100" Height="100"/>
</Grid>
This will apply the appropriate template to each object in your collection. So you can have collection with just facebook contacts or just twitter contacts or mixed.
Also note: You do not need the common interface. It will also work if you just make your ObservableCollection of type object. But given that they are being displayed by the same app in the same list box indicates that you can find some kind of common base and either can create a comon interface or base class.
In your ViewModel, create a property that exposes one or the other ObservableCollection, and swap it out when the button is clicked:
private ObservableCollection<string> _twitterFriendList;
private ObservableCollection<string> _facebookFriendList;
private ObservableCollection<string> _selectedFriendList;
public ObservableCollection<string> SelectedFriendList
{
get { return _selectedFriendList; }
set
{
if (value != _selectedFriendList)
{
_selectedFriendList = value;
RaisePropertyChanged("SelectedFriendList");
}
}
}
void TwitterButton_Click(object sender, RoutedEventArgs e)
{
SelectedFriendList = _twitterFriendList;
}
void FacebookButton_Click(object sender, RoutedEventArgs e)
{
SelectedFriendList = _facebookFriendList;
}
Then in your XAML you can just bind to the property:
<ListBox ItemsSource="{Binding SelectedFriendList}"/>
A non-elegant way of accomplishing this is to put 2 listboxes in the same location and bind 1 to the twitter collection and the other to the facebook collection. Bind their visibility to a property that changes based upon the button clicks. Personally, I'd have 2 radio buttons and display the listbox based upon which one is selected.
<ListBox Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=TwitterCollection}" SelectedItem="{Binding Path=SelectedTwitterItem}" Visibility="{Binding Path=IsTwitterSelected, Converter={StaticResource visibilityConverter}}" />
<ListBox Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=FacebookCollection}" SelectedItem="{Binding Path=SelectedFacebookItem}" Visibility="{Binding Path=IsFacebookSelected, Converter={StaticResource visibilityConverter}}" />
<RadioButton Grid.Row="1" Grid.Column="0" GroupName="rdoOptions" Content="{Binding Path=TwitterLabel}" IsChecked="{Binding Path=IsTwitterSelected}" />
<RadioButton Grid.Row="1" Grid.Column="1" GroupName="rdoOptions" Content="{Binding Path=FacebookLabel}" IsChecked="{Binding Path=IsFacebookSelected}" />

Categories