Error on binding table value to Combobox - wpf - c#

I have a combobox which should bind the data dynamically from the database .
The source of the combobox is a observable collection.
Steps I followed:
Declared a combobox :
<ComboBox ItemsSource="{Binding populatecombobox.modeltogetusername }" Width="155" Margin="18,15,618,0"/>
Created a class to get the data from the database :
public class populatetab2combobox
{
public ObservableCollection<comboboxdata> modeltogetusername { get; set; }
public void getdatausinglinq()
{
using (Operations_Productivity_ToolEntities context = new Operations_Productivity_ToolEntities())
{
var a1 = from t1 in context.Test_ImportedAuditdata
select t1;
if (modeltogetusername == null)
modeltogetusername = new ObservableCollection<comboboxdata>();
foreach (var a in a1.GroupBy(x => x.username).Select(x => x.FirstOrDefault()))
{
modeltogetusername.Add(new comboboxdata
{
username = a.username
});
}
}
}
}
Instantiating the above mentioned class in viewmodel
public class ViewModel: INotifyPropertyChanged {
private populatetab2combobox _populatecombobox = new populatetab2combobox();
public populatetab2combobox populatecombobox {
get {
return _populatecombobox;
}
set {
if (value != _populatecombobox) {
_populatecombobox = value;
OnPropertyChanged("populatecombobox");
}
}
}
public ViewModel() {
_populatecombobox.getdatausinglinq();
}
}
The expected output is :
Ren1
Ren2
The actual output is
Namespace.Model.comboxdata
Namespace.Model.comboxdata

You are getting the output of the ToString() method and you are binding to instances of comboboxdata class and not the username inside of it.
You have 2 options.
First you can change your xaml to this notice how we bind to the property in the item template.
<ComboBox ItemsSource="{Binding populatecombobox.modeltogetusername }" Width="155" Margin="18,15,618,0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding username}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Second you can override the ToString() method on comboboxdata to return the username

Related

Get checked items from a listbox

I'm just getting used to MVVM and want to do without the code-behind and define everything in the view-models.
the combobox represents several selection options (works). I would like to query the elements that have been checked.
Unfortunately I can't access them. The textbox should display all selected elements as concatenated string.
View-Model
class MainViewModel : BaseViewModel
{
#region Fields
private ObservableCollection<EssayTypeViewModel> _essayTypes;
private EssayTypeViewModel _selectedEssayTypes;
#endregion
public ObservableCollection<EssayTypeViewModel> EssayTypes
{
get => _essayTypes;
set
{
if (_essayTypes == value) return;
_essayTypes = value; OnPropertyChanged("EssayTypes");
}
}
public EssayTypeViewModel SelectedEssayTypes
{
get => _selectedEssayTypes;
set { _selectedEssayTypes = value; OnPropertyChanged("SelectedEssayTypes"); }
}
public MainViewModel()
{
// Load Essay Types
EssayTypeRepository essayTypeRepository = new EssayTypeRepository();
var essayTypes = essayTypeRepository.GetEssayTypes();
var essayTypeViewModels = essayTypes.Select(m => new EssayTypeViewModel()
{
Text = m.Text
});
EssayTypes = new ObservableCollection<EssayTypeViewModel>(essayTypeViewModels);
}
}
XAML
<ListBox x:Name="Listitems" SelectionMode="Multiple" Height="75" Width="200" ItemsSource="{Binding EssayTypes}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" IsChecked="{Binding Checked}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Text="{Binding Path=SelectedEssayTypes}" Grid.Column="0" Width="160" Height="25" Margin="0,140,0,0"/>
You could hook up an event handler to the PropertyChanged event of all EssayTypeViewModel objects in the EssayTypes collection and raise the PropertyChanged event for a read-only property of the MainViewModel that returns all selected elements as concatenated string:
public MainViewModel()
{
// Load Essay Types
EssayTypeRepository essayTypeRepository = new EssayTypeRepository();
var essayTypes = essayTypeRepository.GetEssayTypes();
var essayTypeViewModels = essayTypes.Select(m =>
{
var vm = EssayTypeViewModel()
{
Text = m.Text
};
vm.PropertyChanged += OnPropertyChanged;
return vm;
});
EssayTypes = new ObservableCollection<EssayTypeViewModel>(essayTypeViewModels);
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Checked")
OnPropertyChanged("SelectedItems");
}
public string SelectedItems => string.Join(",", EssayTypes.Where(x => x.Checked).ToArray());
This requires the EssayTypeViewModel class to implement the INotifyPropertyChanged interface (by for example deriving from your BaseViewModel class).
You can apply Mode = Two way on the checkbox binding.
<CheckBox Content="{Binding Text}" IsChecked="{Binding Checked, Mode=TwoWay}"/>
then you can iterate through the essay types collection to check if the item entry was checked.
For ex. Sample code can be:
foreach (var essayTypeInstance in EssayTypes)
{
if(essayTypeInstance.Checked)
{
// this value is selected
}
}
Hope this helps.
mm8 answer works. In the meantime i came up with another approach. Not 100% MVVM compatible but it works and is quite simple.
XAML
<ListBox x:Name="ListItems" SelectionMode="Multiple" Height="75" Width="200" ItemsSource="{Binding CollectionOfItems}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding Checked, Mode=TwoWay}" Unchecked="GetCheckedElements" Checked="GetCheckedElements" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Text="{Binding SelectedItemsString, UpdateSourceTrigger=PropertyChanged}" Grid.Column="0" Width="160" Height="25" Margin="0,140,0,0"/>
Code Behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
private void GetCheckedElements(object sender, RoutedEventArgs e)
{
(DataContext as MainViewModel)?.FindCheckedItems();
(DataContext as MainViewModel)?.ConcatSelectedElements();
}
}
Model
public class Items
{
public bool Checked { get; set; }
public string Name { get; set; }
}
ItemsViewModel (BaseViewModel only implements INotifyPropertyChanged)
class ItemsViewModel : BaseViewModel
{
private bool _checked;
private string _name;
public bool Checked
{
get => _checked;
set
{
if (value == _checked) return;
_checked = value;
OnPropertyChanged("Checked");
}
}
public string Name
{
get => _name;
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged("Name");
}
}
}
MainViewModel
public class MainViewModel : BaseViewModel
{
private string _selectedItemsString;
private ObservableCollection<Items> _selectedItems;
public ObservableCollection<Items> CollectionOfItems { get; set; }
public ObservableCollection<Items> SelectedItems
{
get => _selectedItems;
set
{
_selectedItems = value;
OnPropertyChanged("SelectedItems");
}
}
public string SelectedItemsString
{
get => _selectedItemsString;
set
{
if (value == _selectedItemsString) return;
_selectedItemsString = value;
OnPropertyChanged("SelectedItemsString");
}
}
public MainViewModel()
{
CollectionOfItems = new ObservableCollection<Items>();
SelectedItems = new ObservableCollection<Items>();
CollectionOfItems.Add(new Items { Checked = false, Name = "Item 1" });
CollectionOfItems.Add(new Items { Checked = false, Name = "Item 2" });
CollectionOfItems.Add(new Items { Checked = false, Name = "Item 3" });
}
public void FindCheckedItems()
{
CollectionOfItems.Where(x => x.Checked).ToList().ForEach(y => SelectedItems.Add(y));
}
public void ConcatSelectedElements()
{
SelectedItemsString = string.Join(", ", CollectionOfItems.Where(x => x.Checked).ToList().Select(x => x.Name)).Trim();
}
}

Get item from CollectionViewSource using index number

I'm using CollectionViewSource as ItemSource in DataGrid
<DataGrid
ItemsSource="{Binding CollViewSource.View}"
SelectedIndex="{Binding IndexNumber}"
...
and the CollectionViewSource is bound to ObservableCollection in ViewModel
private ObservableCollection<LevelModel> mLevelSource;
public ObservableCollection<LevelModel> LevelSource
{
get
{
mLevelSource = mLevelSource ?? new ObservableCollection<LevelModel>();
return mLevelSource;
}
}
public CollectionViewSource CollViewSource { get; set; }
Model
public class LevelModel : BaseViewModel
{
public string Level_Title { get; set; }
...
In Constructor
CollViewSource = new CollectionViewSource();
CollViewSource.Source = LevelSource;
I have Button inside DataGrid
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding DataContext.ViewCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}"
Content="View" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
What I want, when i click the Button .i.e ViewCommand it should fetch Level_Title or some other item by Index Number
private ICommand mViewCommand;
public ICommand ViewCommand
{
get
{
if (mViewCommand == null)
{
mViewCommand = new DelegateCommand(delegate ()
{
int indexNumber = IndexNumber;
//string title = // logic should go here
});
}
return mViewCommand;
}
}
For example when index number is 3 then it should fetch the item that exist on 3rd index
Note: I don't want to involve SeletedItem
Try the following on your CollectionView:
LevelModel lm = CollViewSource.View.Cast<LevelModel>().ToArray()[indexNumber];
string title = lm.Level_Title;

View to ViewModel to Settings

Is it possible to refactor the currentDevices into a collection?
Basically, I have three comboboxes in which the selectedvalue is bound to currentDevices. then the currentDevices are taken from a settings file.
View
<ComboBox ItemsSource="{Binding availableDevices}"
SelectedValue="{Binding currentDevice1}">
</ComboBox>
<ComboBox ItemsSource="{Binding availableDevices}"
SelectedValue="{Binding currentDevice2}">
</ComboBox>
<ComboBox ItemsSource="{Binding availableDevices}"
SelectedValue="{Binding currentDevice3}">
</ComboBox>
ViewModel
public string currentDevice1 {
get
{
return SampleSettings.Default.Device1;
}
set
{
SampleSettings.Default.Device1 = value;
}
}
public string currentDevice2
{
get
{
return SampleSettings.Default.Device2;
}
set
{
SampleSettings.Default.Device2 = value;
}
}
public string currentDevice3
{
get
{
return SampleSettings.Default.Device3;
}
set
{
SampleSettings.Default.Device3 = value;
}
}
I'd write a DeviceOptionViewModel, and give the main viewmodel an ObservableCollection.
DeviceOptionViewModel.cs
public class DeviceOptionViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _currentDevice;
public String CurrentDevice {
get { return _currentDevice; }
set {
_currentDevice = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(CurrentDevice));
}
}
// Parent event assigns this to his own availableDevices
// when he creates this.
public IEnumerable AvailableDevices { get; set; }
}
Main VM:
public ObservableCollection<DeviceOptionViewModel>
CurrentDevices { get; private set; }
= new ObservableCollection<DeviceOptionViewModel>();
XAML:
<ItemsControl
ItemsSource="{Binding CurrentDevices}"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- DataContext here is DeviceOptionViewModel. We gave it its
own reference to AvailableDevices to simplify binding. -->
<ComboBox
ItemsSource="{Binding AvailableDevices}"
SelectedValue="{Binding CurrentDevice}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Back to main viewmodel:
protected void PopulateCurrentDevices(IEnumerable<String> stringsFromWherever)
{
CurrentDevices.Clear();
foreach (var device in stringsFromWherever)
{
var dovm = new DeviceOptionViewModel() {
CurrentDevice = device,
AvailableDevices = this.availableDevices
};
dovm.PropertyChanged += DeviceOptionViewModel_PropertyChangedHandler;
CurrentDevices.Add(dovm);
}
}
protected void DeviceOptionViewModel_PropertyChangedHandler(object sender,
PropertyChangedEventArgs e)
{
var dopt = sender as DeviceOptionViewModel;
if (e.PropertyName == nameof(DeviceOptionViewModel.CurrentDevice))
{
// Do stuff
}
}
So you populate and repopulate CurrentDevices in your viewmodel as needed, and the UI will magically appear if all the notifications are done correctly.
If you create a new ObservableCollection and assign that to the CurrentDevices property, you'll need to raise PropertyChanged(nameof(CurrentDevices)) on the main viewmodel. I made the setter private to avoid having to implement that detail. If it's not a huge collection, may as well just Clear() and Add() on the same old instance.

Getting Only one Selected Item from Listbox

I am working with Listbox in wpf.My problem is I am getting only one selected item from listbox.I want to get all the item which is selected but don't know how to implement ? please help!
Here is my code :
<ListBox ItemsSource="{Binding Markets}" BorderThickness="0" Background="{x:Null}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Multiple" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Style="{DynamicResource CheckBoxStyle1}" Width="140" Margin="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is my viewmodel content:
private string _selectedItem;
public List<string> Markets { get; set;}
public SettingVM()
{
Markets = new List<string>();
Markets.Add("United Kingdom");
Markets.Add("Australia");
}
public string SelectedItem
{
get
{
return _selectedItem;
}
set
{
Set(() => SelectedItem, ref _selectedItem, value);
}
}
You can bind directly the SelectedItem listobx property to your view model but not the SelectedItems property.
I used a behavior to achieve this associated with a delegate command (included in the Prism framework)
Your object
class MyObject
{
}
The behavior
internal class MyObjectSelectionChangedToCommand
{
public static readonly DependencyProperty SelectionCommandProperty =
DependencyProperty.RegisterAttached("SelectionCommand", typeof(DelegateCommand<GridSelectionInfo<MyObject>>),
typeof(ResourceCardGridSelectionChangedToCommand), new PropertyMetadata(null, OnSelectionCommandChanged));
public static DelegateCommand<GridSelectionInfo<MyObject>> GetSelectionCommand(DependencyObject obj)
{
return (DelegateCommand<GridSelectionInfo<MyObject>>)obj.GetValue(SelectionCommandProperty);
}
public static void SetSelectionCommand(DependencyObject obj, DelegateCommand<GridSelectionInfo<MyObject>> value)
{
obj.SetValue(SelectionCommandProperty, value);
}
private static void dg_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var tsci = new GridSelectionInfo<MyObject>
{
Added = e.AddedItems.Cast<MyObject>().ToList(),
Removed = e.RemovedItems.Cast<MyObject>().ToList(),
Selected = ((ListBox)sender).SelectedItems.Cast<MyObject>().ToList()
};
var cmd = GetSelectionCommand((DependencyObject)sender);
cmd.Execute(tsci);
}
private static void OnSelectionCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dg = d as ListBox;
if (dg != null) dg.SelectionChanged += dg_SelectionChanged;
}
}
public class GridSelectionInfo<T>
{
public GridSelectionInfo()
{
Selected = new List<T>();
Added = new List<T>();
Removed = new List<T>();
}
public List<T> Added { get; set; }
public List<T> Removed { get; set; }
public List<T> Selected { get; set; }
public override string ToString()
{
return string.Format("Added: {0}, Removed: {1}, Selected: {2}", Added.Count, Removed.Count, Selected.ToFormattedString());
}
}
The XAML part where you bind the view model command to the behavior
<ListBox ItemsSource="{Binding MyCollection}"
resources:ResourceCardGridSelectionChangedToCommand.SelectionCommand="{Binding CmdObjectSelectionChanged}">
</ListBox>
Then, in you view model you just have to declare the command
public DelegateCommand<GridSelectionInfo<MyObject>> CmdObjectSelectionChanged { get; private set; }
And create it
CmdObjectSelectionChanged = new DelegateCommand<<GridSelectionInfo<MyObject>>(ExecuteSelect,CanExecuteSelect);
Thus, every time the selection changes in your listbox, you will receive all the info about selected items in the execute delegate wrapped in the GridSelectionInfo object;
maybe this help you
You can find them in ListBox.SelectedItems. use foreach
foreach (ListItem li in listBox1.SelectedItems)
{
// your code here
}
You use "SelectedItem="{Binding SelectedItem}">". Acording to MSDN "Gets or sets the first item in the current selection or returns null if the selection is empty "
And you need to bind SelectedItems to a list in your viewmodel.
In the ListBox controls, the SelectedItems property is read-only but you can add a behavior that do the job, see this answer : https://stackoverflow.com/a/8369532/1431524
I had the same problem and when I was binding to the SelectedItems the property was not notifying the model in some cases! I ended extending my entity with a non persisted property ("Selected") and then binded this property to the IsCheched property of the CheckBox.
In your case create another partial class file Market to extend the Market object, add a new bool property (e.g. Selected) and then at your checkbox bind this property:
<CheckBox Content="{Binding}" IsChecked="{Binding Selected}" Style="{DynamicResource CheckBoxStyle1}" Width="140" Margin="10"/>

Filtering ObservableCollection with ICollectionView

I have ObservableCollection binded to dataGrid and now I want to filter the presented data I see that I need to use ICollectionView but I am not sure how to add ICollectionView with my MVVM pattern.
My code simplified looks following:
public class MainViewModel : ViewModelBase , IBarcodeHandler
{
public ObservableCollection<TraceDataItem> TraceItemCollectionViewSource { get; set; }
}
My XAML
<Window xmlns:controls="clr-namespace:Mentor.Valor.vManage.RepairStation.Controls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
<DataGrid Grid.Row="2" ColumnWidth="*" ItemsSource="{Binding TraceItemCollectionViewSource , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" RowStyle="{StaticResource TraceRowStyle}" IsReadOnly="True" Name="TraceDataGrid" Margin="5,5,5,5" Padding="5,5,5,5" AutoGenerateColumns="False">
</Window>
How I can add ICollectionView here in order to apply filtering to the view?
You would need to:
public class MainViewModel : ViewModelBase, IBarcodeHandler
{
public ICollectionView TraceItemCollectionView
{
get { return CollectionViewSource.GetDefaultView(TraceItemCollectionViewSource); }
}
public ObservableCollection<TraceDataItem> TraceItemCollectionViewSource { get; set; }
}
then, somewhere in the code (maybe in the constructor) add your filter:
TraceItemCollectionView.Filter = o =>
{
var item = (TraceDataItem) o;
//based on item, return true if it should be visible, or false if not
return true;
};
And, in XAML, you would need to change the binding to TraceItemCollectionView property.
You may invoke the Filter callback from a Command and expose the View property from CollectionViewSource :
public class ViewModel: INotifyPropertyChanged
{
private CollectionViewSource data = new CollectionViewSource();
private ObservableCollection<Child> observableChilds = new ObservableCollection<Child>();
public ViewModel()
{
var model = new Model();
model.ChildList.Add(new Child { Name = "Child 1" });
model.ChildList.Add(new Child { Name = "Child 2" });
model.ChildList.Add(new Child { Name = "Child 3" });
model.ChildList.Add(new Child { Name = "Child 4" });
//Populate ObservableCollection
model.ChildList.ToList().ForEach(child => observableChilds.Add(child));
this.data.Source = observableChilds;
ApplyFilterCommand = new DelegateCommand(OnApplyFilterCommand);
}
public ICollectionView ChildCollection
{
get { return data.View; }
}
public DelegateCommand ApplyFilterCommand { get; set; }
private void OnApplyFilterCommand()
{
data.View.Filter = new Predicate<object>(x => ((Child)x).Name == "Child 1");
OnPropertyChanged("ChildCollection");
}
}
//Sample Model used
public class Model
{
public Model()
{
ChildList = new HashSet<Child>();
}
public ICollection<Child> ChildList { get; set; }
}
public class Child
{
public string Name { get; set; }
}
//View
<ListBox ItemsSource="{Binding Path = ChildCollection}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding ApplyFilterCommand}"/>
A CollectionView is not always the best solution. you can also filter your collection using some simple LinQ. Take this simple example:
public ObservableCollection<TraceDataItem> FilteredData
{
get
{
return new ObservableCollection<TraceDataItem>(YourUnfilteredCollection.Where(
i => MeetsFilterRequirements(i)));
}
}
private bool MeetsFilterRequirements(TraceDataItem item)
{
return item.SomeProperty == someValue || item is SomeType;
}
The beauty of this method is that you can add some complex filtering requirements. One thing to note: whenever any properties in this method are changed, you'd need to call NotifyPropertyChanged("FilteredData") to ensure that the UI will be updated accordingly.

Categories

Resources