WPF ComboBox ItemsSource not working - c#

I am new to WPF, trying to fix up an existing program.
There is a ComboBox defined as:
<ComboBox Height="23" Margin="111.5,6.738,6,0" Name="comboBoxDocType" VerticalAlignment="Top" FontFamily="Calibri" FontSize="11" SelectedIndex="0" SelectionChanged="comboBoxDocType_SelectionChanged" ItemsSource="{Binding}">
Trying to populate it in the code behind:
DocumentTypesList = new List<string>();
DocumentTypesList.Add(DocumentTypes.Unknown);
DocumentTypesList.Add(DocumentTypes.PurchaseOrder);
DocumentTypesList.Add(DocumentTypes.RMInvoice);
DocumentTypesList.Add(DocumentTypes.SundryOne);
DocumentTypesList.Add(DocumentTypes.DevelopmentPaper);
comboBoxDocType.ItemsSource = DocumentTypesList;
ComboBox is coming up with nothing in it. Is there something missing?

ItemsSource="{Binding}" is unnecessary in this context.

This can be done by pure binding. I'm not quite sure what you try to achieve, but the example below shows how you can hook onto on an event and avoid codebehind. You could just bind to SelectedItem as well. However here is an suggestion for your problem.
Xaml:
<ComboBox Height="23" Margin="111.5,6.738,6,0" VerticalAlignment="Top" FontFamily="Calibri" FontSize="11" SelectedIndex="0" ItemsSource="{Binding DocumentTypesList}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<command:EventToCommand Command="{Binding DocumentSelectionChangedCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Note that I am using a viewmodel here, so you must set datacontext of your view to an instance of this class. Personally I would have avoided the event if possible, and instead just bind to the selecteditem(s) and maybe create a behaviour, for avoiding this coupling.
Code:
public class YourViewModel : INotifyPropertyChanged
private ObservableCollection<DocumentTypes> documentTypesList = new ObservableCollection<DocumentTypes> {DocumentTypes.Unknown, DocumentTypes.PurchaseOrder, DocumentTypes.RMInvoice, DocumentTypes.SundryOne, DocumentTypes.DevelopmentPaper};
public ObservableCollection<DocumentTypes> DocumentTypesList
{
get { return documentTypesList; }
set
{
if (Equals(value, documentTypesList)) return;
documentTypesList = value;
OnPropertyChanged();
}
}
public ICommand DocumentSelectionChangedCommand { get; set; }
public YourViewModel()
{
InitStuff();
}
public void InitStuff(){
DocumentSelectionChangedCommand = new RelayCommand<SelectionChangedEventArgs>(OnDocumentChanged);
}
private void OnDocumentChanged(SelectionChangedEventArgs e)
{
// To your stuff here, but all this can be done by bindings as well!
// Invoke in some SelectedDocuments property's setter or something
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator] // Comment out this line if no R#
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Hope it helps,
Cheers,
Stian

public MainPage()
{
this.InitializeComponent();
//get menu
List<listboxitem> menu_list = Load_Menu();
lst_menu.ItemsSource = menu_list;
}
private NavigationHelper NavigationHelper;
private ObservableDictionary DefaultViewmodel = new ObservableDictionary();
private string[] Logo_menu_array = { "Assets/star-6-48.ico", "/Assets/note-48.ico", "/Assets/medal-48.ico", "/Assets/joystick-48.ico" };
private string[] Text_menu_array={"Phổ biến trên YouTuBe","Âm nhạc","Thể thao","Trò chơi"};
//Menu
public class listboxitem
{
public string textmenu { get; set; }
public string logomenu { get; set; }
}
//load menu
public List<listboxitem> Load_Menu()
{
List<listboxitem> text = new List<listboxitem>();
for (int i = 0; i < Math.Min(Logo_menu_array.Length, Text_menu_array.Length); i++)
{
var l = new listboxitem();
l.logomenu = Logo_menu_array[i];
l.textmenu = Text_menu_array[i];
text.Add(l);
}
return text;
}
I hope it will help you :)

Related

Revit API WPF C#: Check All button for Check Boxes in Listbox

I'm pretty new to programming with WPF and C# and I have a question regarding the possibility to automatically check all the CheckBoxes in a Listbox. I'm developing a plugin for Autodesk Revit and, after having listed all the names of the rooms in a list box, I want to check them all using the button "Check All"
I've read the thread at this page but still, I'm not able to make it work. May someone help me with my code?
Here is what I've done:
XAML:
<ListBox x:Name='roomlist'
SelectionMode='Multiple'>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked='{Binding IsChecked}'
Content="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.InputBindings>
<KeyBinding Command="ApplicationCommands.SelectAll"
Modifiers="Ctrl"
Key="A" />
</ListBox.InputBindings>
<ListBox.CommandBindings>
<CommandBinding Command="ApplicationCommands.SelectAll" />
</ListBox.CommandBindings>
</ListBox>
C#
public partial class RoomsDistance_Form : Window
{
UIDocument _uidoc;
Document _doc;
public RoomsDistance_Form(Document doc, UIDocument uidoc)
{
InitializeComponent();
FilteredElementCollector collector = new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.OfCategory(BuiltInCategory.OST_Rooms);
List<String> myRooms = new List<String>();
foreach (var c in collector)
{
myRooms.Add(c.Name);
}
myRooms.Sort();
roomlist.ItemsSource = myRooms;
}
private void checkAllBtn_Click(object sender, RoutedEventArgs e)
{
foreach (CheckBox item in roomlist.Items.OfType<CheckBox>())
{
item.IsChecked = true;
}
}
public class Authority : INotifyPropertyChanged
{
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Thank you very much for your help!
In the thread you are linking to, they are setting the "IsChecked" on the data object (Authority), not the CheckBox control itself.
foreach (var a in authorityList)
{
a.IsChecked = true;
}
You have a binding to IsChecked that will update the Checkbox control when NotifyPropertyChanged() is called.
After having lost my mind in the effort i solved my problem by avoiding the Listbox.. I simply added single CheckBoxes in the StackPanel.
XAML:
<ScrollViewer Margin='10,45,10,100'
BorderThickness='1'>
<StackPanel x:Name='stack'
Grid.Column='0'></StackPanel>
</ScrollViewer>
C#:
foreach (var x in myRooms)
{
CheckBox chk = new CheckBox();
chk.Content = x;
stack.Children.Add(chk);
}
Not what i was looking for but now it works and that's the point.
Thank you for your help!
I usually use CheckBoxList in the following way:
In xaml:
<ListBox ItemsSource="{Binding ListBoxItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> //+some dimensional properties
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In xaml.cs:
public partial class MyWindow : Window
{
public ViewModel ViewModel {get; set; }
public MyWindow(ViewModel viewModel)
{
//keep all the mess in ViewModel, this way your xaml.cs will not end up with 1k lines
ViewModel = viewModel;
DataContext = ViewModel;
InitializeComponent();
}
void BtnClick_SelectAll(object sender, RoutedEventArgs e)
{
ViewModel.CheckAll();
}
}
ViewModel preparation:
public class ViewModel
{
public List<ListBoxItem> ListBoxItems { get; set; }
//InitializeViewModel()...
//UpdateViewModel()...
//other things....
public void CheckAll()
{
foreach (var item in ListBoxItems)
{
item.IsSelected = true;
}
}
public class ListBoxItem : INotifyPropertyChanged
{
public string Name { get; set; }
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged(nameof(IsSelected));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

creating dynamic controls with MVVM

I am trying to create a dynamic control with using mvvm for the first time. I want to generate buttons dynamically and have the content display inside of the buttons. I am sure I am missing something really easy here, but I have no idea what it could be. When I run the code, nothing appears on the interface, though I can see AvailableMonitorOC populate in the constructor...
Here is my ViewModel where I manually add buttons to an observable collection for simplicity sake of this example:
public class CreateAndDisplayViewModel {
public ObservableCollection<AvailableMonitorBo> AvailableMonitorOC = new ObservableCollection<AvailableMonitorBo>();
public CreateAndDisplayViewModel() {
availableMonitorBo = new AvailableMonitorBo();
availableMonitorBo.AvailableMonitorLabel = "Label 1";
AvailableMonitorOC.Add(availableMonitorBo);
availableMonitorBo.AvailableMonitorLabel = "Label 2";
AvailableMonitorOC.Add(availableMonitorBo);
}
private AvailableMonitorBo availableMonitorBo;
public AvailableMonitorBo AvailableMonitorBo {
get { return availableMonitorBo; }
set {
availableMonitorBo = value;
}
}
}
Here is my model:
public class AvailableMonitorBo : INotifyPropertyChanged {
private string availableMonitorLabel { get; set; }
public string AvailableMonitorLabel {
get { return availableMonitorLabel; }
set {
availableMonitorLabel = value;
OnPropertyChanged("AvailableMonitorLabel");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And here is the xaml:
<ListView Grid.Row="2" Grid.Column="1"
ItemsSource="{Binding AvailableMonitorOC, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding AvailableMonitorLabel}"
Width="100"
Height="25"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The main reason for you lack of display is that AvailableMonitorOC needs to be a property of CreateAndDisplayViewModel, not a field as it is currently.
You're also only creating one AvailableMonitorBo instance and changing its caption each time.

Bind Interface in WPF for ComboBox SelectedItem

I've read all over the place, that binding is doable in WPF to Interfaces, but I'm having a heck of a time actually getting any traction with it. I'm using EF Core also, if it helps you ready my code. The ComboBox fills with data, so the bind of the data works, but SelectedItem fails to bind, and the text within the selected item shows blank.
I don't get how the following, binds to the object that implements the interface.
The XAML for ComboBox:
<ComboBox Height="23" x:Name="cbJumpList" Width="177" Margin="2" HorizontalAlignment="Left"
IsEditable="False"
DisplayMemberPath="Name"
SelectedItem="{Binding Path=(model:IData.SelectedJumpList), Mode=TwoWay}"
/>
MainWindow.xaml.cs:
protected IData DB { get; private set; }
public MainWindow()
{
InitializeComponent();
DB = new Data.DataSQLite(true);
DB.Bind_JumpLists_ItemsSource(cbJumpList);
}
IData.cs:
public interface IData : IDisposable, INotifyPropertyChanged
{
void Bind_JumpLists_ItemsSource(ItemsControl control);
IJumpList First_JumpList();
IJumpList SelectedJumpList { get; set; } // TwoWay Binding
}
IJumpList.cs
public interface IJumpList
{
long JumpListId { get; set; }
string Name { get; set; }
}
Then within the implemented object (Data.DataSQLite):
public void Bind_JumpLists_ItemsSource(ItemsControl control)
{
control.ItemsSource = null;
db.JumpLists.ToList();
control.ItemsSource = db.JumpLists.Local;
control.Tag = db.JumpLists.Local;
SelectedJumpList = db.JumpLists.FirstOrDefault();
}
public IJumpList SelectedJumpList
{
get { return _SelectedJumpList; }
set
{
_SelectedJumpList = value;
NotifyPropertyChanged();
}
}
IJumpList _SelectedJumpList;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I should add, the PropertyChanged event remains null.
The SelectedItem property of a ComboBox is supposed to be bound to a property and not to a type. For the binding to work you should also set the DataContext of the ComboBox to an instance of the type where this property is defined.
Try this:
<ComboBox Height="23" x:Name="cbJumpList" Width="177" Margin="2" HorizontalAlignment="Left"
IsEditable="False"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedJumpList}" />
public void Bind_JumpLists_ItemsSource(ItemsControl control)
{
db.JumpLists.ToList();
control.DataContext = this;
control.ItemsSource = db.JumpLists.Local;
control.Tag = db.JumpLists.Local;
SelectedJumpList = db.JumpLists.FirstOrDefault();
}

can you data bind a ListBox with an IEnumerable<type>

I have the following xaml:
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="OrganisationTemplate">
<Grid Margin="40,0,0,0">
<StackPanel>
<TextBlock Text="{Binding name}"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<phone:PanoramaItem Header="Organisations">
<ListBox Name="Organisation" ItemTemplate="{StaticResource OrganisationTemplate}" DataContext="{Binding Organisations, Mode=OneWay}" Margin="0,0,0,96"/>
</phone:PanoramaItem>
and I would like to know if the binding "Organisations" needs to be a List or ICollection or something... Or can it be an IEnumerable. It's just it currently isn't working.
class DashboardViewModel
{
private OrganisationRepository organisationRepository { get; set; }
public IEnumerable<Organisation> Organisations { get; set; }
public DashboardViewModel()
{
LoadOrganisationSection();
}
private async void LoadOrganisationSection()
{
organisationRepository = new OrganisationRepository();
Organisations = await organisationRepository.GetAll();
OnPropertyChanged("Organisations");
//LoadBar loadBar = new LoadBar("Logging in");
//loadBar.ShowLoadBar(this);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler tempEvent = PropertyChanged;
if (tempEvent != null)
{
// property changed
tempEvent(this, new PropertyChangedEventArgs(propertyName));
}
}
}
EDIT:://
public IEnumerable<Organisation> Organisations
{
get
{
return new Organisation[] { new Organisation { name = "hi" } };
}
set
{
OnPropertyChanged();
}
}
If I do this, I get something back out, so it's the trigger that's not working. any ideas how i'm to do this? when my await on organisationRepository.GetAll() finishes.. I need the onchange to happen and update. doh
Try setting the listbox binding to the ItemsSource instead of setting its datacontext. (assuming your viewmodel is already set as the datacontext of the view)

EASY way to refresh ListBox in WPF?

I have created a simple form that inserts/updates/deletes a values for Northwind Customers.
Everything works fine, except in order to see a results, I have to close it, and reopen again.
My form looks like this :
I've searched tens of articles on how to refresh ListBox, but all of those use interface implementing, or using DataSets, and stuff I have never heard of and cannot implement. It's a very simple project, using simple procedures. Is there an easy way to refresh the list of customers without adding many lines of code?
The simple answer is: myListBox.Items.Refresh();
Are you using ObservableCollection and does your model implement INotifyPropertyChanged these two things will automaticly update the ListBox on any change. no need to explicitly refresh the list.
Here is a small example of using ObservableCollection and INotifyPropertyChanged, obviously you will populate your ObservableCollection from your SQL database.
Window:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<MyModel> _list = new ObservableCollection<MyModel>();
private MyModel _selectedModel;
public MainWindow()
{
InitializeComponent();
List.Add(new MyModel { Name = "James", CompanyName = "StackOverflow"});
List.Add(new MyModel { Name = "Adam", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Chris", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Steve", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Brent", CompanyName = "StackOverflow" });
}
public ObservableCollection<MyModel> List
{
get { return _list; }
set { _list = value; }
}
public MyModel SelectedModel
{
get { return _selectedModel; }
set { _selectedModel = value; NotifyPropertyChanged("SelectedModel"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml
<Window x:Class="WpfApplication11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Name="UI">
<Grid>
<ListBox ItemsSource="{Binding ElementName=UI, Path=List}" SelectedItem="{Binding ElementName=UI, Path=SelectedModel}" Margin="0,0,200,0" DisplayMemberPath="DisplayMember" SelectedIndex="0" />
<StackPanel HorizontalAlignment="Left" Height="100" Margin="322,10,0,0" VerticalAlignment="Top" Width="185">
<TextBlock Text="Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Company Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.CompanyName, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Window>
Model
public class MyModel : INotifyPropertyChanged
{
private string _name;
private string _companyName;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public string CompanyName
{
get { return _companyName; }
set { _companyName = value; NotifyPropertyChanged("CompanyName"); }
}
public string DisplayMember
{
get { return string.Format("{0} ({1})", Name, CompanyName); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
PropertyChanged(this, new PropertyChangedEventArgs("DisplayMember"));
}
}
}
In this case any edit to properties will Update your list instantly, also will update when new Items are added/removed.
How about calling ListBox.UpdateLayout?
Of course you also need to update the particular item(s) so that it returns the updated string from the ToString method.
UPDATE: I think you also need to call ListBox.InvalidateArrange before you call ListBox.UpdateLayout.
Use INotifyPropertyChanged is the best way, refresh the entire list is not a good idea.
Main entrance:
public partial class MainWindow : Window
{
private BindingList<FoodModel> foodList = new BindingList<FoodModel>();
public MainWindow()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
foodList.Add(new FoodModel { foodName = "apple1" });
foodList.Add(new FoodModel { foodName = "apple2" });
foodList.Add(new FoodModel { foodName = "apple3" });
FoodListBox.ItemsSource = foodList;
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
foodList[0].foodName = "orange";
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
FoodListBox.Items.Refresh();
}
}
Model:
public class FoodModel: INotifyPropertyChanged
{
private string _foodName;
public string foodName
{
get { return _foodName; }
set
{
if (_foodName != value)
{
_foodName = value;
PropertyChanged(this, new PropertyChangedEventArgs("foodName"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
XAML:
<ListBox HorizontalAlignment="Center" Name="FoodListBox" VerticalAlignment="Top" Width="194" Height="150">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding foodName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Categories