Listbox databinding NewItemPlaceholder - c#

I have an observable collection bound to a list box.
The collection has 2 items, but the list box is showing 3 items (e.g. the 2 items that are actually in the observable collection and an additional item for the NewItemPlaceholder.
I want it only to show the 2 items.
Below is my XAML.
<ListBox MinHeight="20" MinWidth="20" Name="MultipleSelectionsMultipleWagersListBox" Visibility="{Binding Path=Coupon.BarcodeText, Converter={StaticResource CouponBarcodeToVisibilityConverter1}, ConverterParameter=994450_994550}" Height="AUto" Width="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="5"
ItemsSource="{Binding Path=BetViewModels}" Grid.Row="1" Grid.Column="1" >
<ListBox.ItemTemplate>
<DataTemplate>
<View:BetView DataContext="{Binding}" Name="ThisBet" Margin="5"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is the c#
private ObservableCollection<BetViewModel> _betViewModels = new ObservableCollection<BetViewModel>();
public ObservableCollection<BetViewModel> BetViewModels
{
get { return _betViewModels; }
set
{
if (Equals(value, _betViewModels)) return;
_betViewModels = value;
OnPropertyChanged("BetViewModels");
}
}
Here is the code to populate the betViewModels:
var betViewModel = new BetViewModel { Bet = new Bet() };
betViewModel.Bet.SelectionName = "Chelsea";
betViewModel.Bet.Price = "4/9";
betViewModel.Bet.Market = "90 Minutes";
betViewModel.Bet.ExpectedOdd = DateTime.Now;
BetViewModels.Add(betViewModel);
betViewModel = new BetViewModel { Bet = new Bet() };
betViewModel.Bet.SelectionName = "Chelsea";
betViewModel.Bet.Price = "4/9";
betViewModel.Bet.Market = "90 Minutes";
betViewModel.Bet.ExpectedOdd = DateTime.Now;
BetViewModels.Add(betViewModel);
How Do I switch of this from showing the additional item for the new item place
Here is an image of it displaying the placeholder

The DataGrid supports adding new rows, which have to start out blank. If your ItemsSource is bound to both a ListBox/ItemsControl and a DataGrid, you need to set the DataGrid 'CanUserAddRows' property to 'False'.
Where I found the answer: http://www.mindstick.com/Forum/1519/How%20do%20I%20remove%20a%20listbox%20new%20item%20placeholder

There's nothing in your code that should be adding an extra empty item. There may be some other code adding to BetViewModels or there may be a change happening to the generated ICollectionView for the collection if you have it bound to something else that you're not showing, like an editable DataGrid.

did your sample code also provide this issue?
how much items contains your _betViewModels.count in debugging there are really only 2 Items?
it seems you added an empty BetViewModel at the End
i would suggest check your logic which provides populates your items
if it is a loop it should (counter<yourDatasource.Count) just for example

Related

c# - UWP ListView displays incorrect items upon rapid scrolling when it has a DataTemplate

I have a ListView that is intended to show every product within a database, and it works for the most part, but when I scroll down by dragging the scroll bar, the bottom items end up being incorrect.
XAML Definition:
<ListView x:Name="lst_Products" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="16,124,16,16" Width="300" ContainerContentChanging="lst_Products_ContainerContentChanging" Loaded="lst_Products_Loaded" BorderBrush="Black" BorderThickness="2" CornerRadius="16">
<ListView.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Value}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The data template is present so I can easily grab a product ID number with SelectedValue. According to some trusted community member (or whatever they call the prominent posters) on the MSDN forums said that's the only way to properly show a ListView when the ItemsSource is an ObservableCollection<KeyValuePair<int,RelativePanel>> while having a selectable value member.
The relevant C# code:
private async void lst_Products_Loaded(object sender, RoutedEventArgs e)
{
var products = await ProductManager.GetProducts();
ObservableCollection<KeyValuePair<int, RelativePanel>> productList = new(products);
lst_Products.ItemsSource = productList;
lst_Products.SelectedValuePath = "Key";
}
private void lst_Products_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.ItemIndex % 2 == 1)
{
args.ItemContainer.Background = new SolidColorBrush(Color.FromArgb(128, 128, 128, 128));
}
else
{
args.ItemContainer.Background = UIManager.GetDefaultBackground();
}
}
public static async Task<List<KeyValuePair<int, RelativePanel>>> GetProducts()
{
var productPanels = new List<KeyValuePair<int, RelativePanel>>();
var productIDs = await SqlHandler.ReturnListQuery<int>($"SELECT id FROM {productTable}");
var productNames = await SqlHandler.ReturnListQuery<string>($"SELECT name FROM {productTable}");
var panels = new List<RelativePanel>();
foreach(var name in productNames)
{
RelativePanel panel = new();
TextBlock productName = new()
{
Text = name
};
panel.Children.Add(productName);
panels.Add(panel);
}
for(int i = 0; i < productIDs.Count; i++)
{
productPanels.Add(new KeyValuePair<int, string>(productIDs[i], panels[i]));
}
return productPanels;
}
The call to SQL Handler just runs an SQL query and returns a list of the results. I can post the code if you need, but I can assure you there's no sorting going on.
A screenshot of what the list looks like. The bottom item should be "Coffee" - Button Test Product 2 is the second item in the list.
A screenshot of the SQL datatable with the "Coffee" product at the bottom where it should be.
In this case it's just the bottom item that's incorrect, however other times it has jumbled 5 or 6 entries near the bottom. This only seems to occur with the DataTemplate/ContentPresenter, but without that, the RelativePanel does not display correctly in the list. Eventually the list will show more information about the product and as far as I can tell, there's no good way to do that without converting the SQL data into a RelativePanel on the c# side.
I'm open to suggestions on solving either the jumbling problem with the template, or adjusting the xaml so that I don't need the template to display bulk sql data without needing the template but I'm at a loss.
c# - UWP ListView displays incorrect items upon rapid scrolling when it has a DataTemplate
The problem should be caused by listview virtualization, There are two ways to sloved this prolbem, one is disalbe listview virtualization by setting ItemsPanel as StackPanel like the following
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
And the other way is implement INotifyCollectionChanged interface for your model class. for more please refer to Data binding in depth
It's not good practice that useRelativePanel collection as datasoure, the better way is make RelativePanel in your DataTemplate and bind with mode class property.
For example
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Index}" />
<TextBlock Text="{Binding IsItem}" />
<Image Source="{Binding ImageSource}" Visibility="Collapsed" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>

ListBox filled with binding doesn't select item on click

I'm trying to use a ListBox to choose an entry and then display a picture belonging to this selected entry. But just at the beginning I got my first problem: filling the ListBox with binding is working, but if I click on one line in my running program, it doesn't select the line. I can just see the highlighted hover effect, but not select a line. Any ideas what my mistake could be?
This is my XAML:
<ListBox x:Name="entrySelection" ItemsSource="{Binding Path=entryItems}" HorizontalAlignment="Left" Height="335" Margin="428,349,0,0" VerticalAlignment="Top" Width="540" FontSize="24"/>
And in MainWindow.xaml.cs I'm filling the ListBox with entries:
private void fillEntrySelectionListBox()
{
//Fill listBox with entries for active user
DataContext = this;
entryItems = new ObservableCollection<ComboBoxItem>();
foreach (HistoryEntry h in activeUser.History)
{
var cbItem = new ComboBoxItem();
cbItem.Content = h.toString();
entryItems.Add(cbItem);
}
this.entrySelection.ItemsSource = entryItems;
labelEntrySelection.Text = "Einträge für: " + activeUser.Id;
//show image matching the selected entry
if (activeUser.History != null)
{
int index = entrySelection.SelectedIndex;
if (index != -1 && index < activeUser.History.Count)
{
this.entryImage.Source = activeUser.History[index].Image;
}
}
}
So I can see my ListBox correctly filled, but not select anything - so I can't go on with loading the picture matching the selected entry.
I'm still quite new to programming, so any help would be great :)
EDIT: If someone takes a look at this thread later: here's the - quite obvious -solution
XAML now looks like this
<ListBox x:Name="entrySelection" ItemsSource="{Binding Path=entryItems}" HorizontalAlignment="Left" Height="335" Margin="428,349,0,0" VerticalAlignment="Top" Width="540" FontFamily="Siemens sans" FontSize="24">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind to fill it:
//Fill listbox with entries for selected user
DataContext = this;
entryItems = new ObservableCollection<DataItem>();
foreach (HistoryEntry h in selectedUser.History)
{
var lbItem = new DataItem(h.toString());
entryItems.Add(lbItem);
}
this.entrySelection.ItemsSource = entryItems;
labelEntrySelection.Text = "Einträge für: " + selectedUser.Id;
And new Class DataItem:
class DataItem
{
private String text;
public DataItem(String s)
{
text = s;
}
public String Text
{
get
{
return text;
}
}
}
You are filling it with ComboBoxItem, which is not relevant to the ListBox, and also wrong by definition.
You need to have the ObservableCollection filled with data items.
Meaning, make a class that contains the data you want to store, and the ListBox will generate a ListBoxItem automatically per data item.
http://www.wpf-tutorial.com/list-controls/listbox-control/

Remove data from a bound list C#

i am currently trying to remove items from a bound list.
Here is where it is bound in the xaml.
<ListBox Height="362" HorizontalAlignment="Left" Margin="6,245,0,0" Name="lstHoldCategories" VerticalAlignment="Top" Width="462" SelectionChanged="list_SelectionChanged_1" BorderThickness="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<!--This positions the Text eg name etc-->
<StackPanel Orientation ="Vertical">
<!--This changes the size of the photo on the left-->
<Image Width="445" Height="300" HorizontalAlignment="Center" Stretch="UniformToFill" >
<Image.Source>
<BitmapImage UriSource="{Binding imgSource}"/>
</Image.Source>
</Image>
<TextBlock Text="{Binding Name}" Style="{StaticResource PhoneTextLargeStyle}" Width="1000" />
<TextBlock Text="{Binding Type}" Style="{StaticResource PhoneTextLargeStyle}" Width="1000" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have then made a seperate generic list to be held in a seperate unbound listBox so that i can select a "Type" and load up all the animals of that type.
Here is the code where i set up the unbound list
public CategorySearch()
{
InitializeComponent();
observablePets = new ObservableCollection<Shop>();
temp = new ObservableCollection<Shop>();
MyList.Add("Dog");
MyList.Add("Cat");
MyList.Add("Fish");
MyList.Add("Lizard");
lstCategory.ItemsSource = MyList;
}
and this is where i have done the SelectedIndex of the unbound listBox to add in the animals of the selected "Type"
private void lstCategory_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lstCategory.SelectedIndex == 0)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Dog")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 1)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Cat")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 2)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Fish")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 3)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Lizard")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
}
As you can see in this piece of code, I have commented out the piece of code that i believed would empty the listBox on the selectedIndex and reload the listBox with the new selection. Unfortunately it doesn't work and crashes the app when you select an index.
If there is a different way to empty the listBox that is bound, i would appreciate someone advising me how to do it,
Thanks in advance,
Jason
////Pics\\
This is what the page looks like before an index is selected
This is what the bound listBox will look like when you select an index
You need to clear the collection itself, rather than the object bound to the collection. A quick search showed up this... Delete all items from listobox
Just to clarify, the Items collection lives on the ListBox and that property is readonly. So you need to remove the items from the collection your ListBox is actually bound to.
you should just be able to call clear on temp prior to adding you new items. But you will need to make sure your collection source implements the INotifyCollectionChanged to see the changes reflected in the UI.

c# How to get Listbox selection from Observable Collection

I'm probably not even asking this correctly, I am new to c#, but trying to help my 14 year-old son learn. I've created a listbox with items created with an ObservableCollection. Here is the XAML:
<ListBox x:Name="listBox1" ItemsSource="{Binding}" Margin="105,205,886,63"
IsTabStop="True" SelectionChanged="PrintText"
ScrollViewer.VerticalScrollBarVisibility="Hidden" TabIndex="5" FontSize="36"
Background="Transparent" Foreground="#FF55B64C" FontFamily="Arabic Typesetting"
FontWeight="Bold" IsDoubleTapEnabled="False" SelectionMode="Single" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="blockNameList" Text="{Binding name}"/>
<TextBlock Text=" #"/>
<TextBlock Name="blockIdList" Text="{Binding id}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is how I created the ListBox Items:
var client = new HttpClient();
var uri = new Uri("http://theurlImusing");
Stream respStream2 = await client.GetStreamAsync(uri);
// DataContractJsonSerializer ser2 = new DataContractJsonSerializer(typeof(RootObject));
// RootObject feed2 = (RootObject)ser2.ReadObject(respStream2);
DataContractJsonSerializer ser = null;
ser = new DataContractJsonSerializer(typeof(ObservableCollection<RootObject>));
ObservableCollection<RootObject> feed2 = ser.ReadObject(respStream2) as ObservableCollection<RootObject>;
var cardList = new List<RootObject>();
foreach (RootObject returnfeed in feed2)
{
string cid = returnfeed.id;
string cardname = returnfeed.name;
listBox1.Items.Add(new RootObject { id=cid, name=cardname });
}
I thought I would just use the SelectionChanged="PrintText" property of the listbox so that when I clicked on a listbox item, it would just change a textblock's text value. Ultimately, that is all I am trying to do...set a textblock or textbox to be equal to the "id" value that is clicked on in the ListBox.
void PrintText(object sender, SelectionChangedEventArgs args)
{
//What do I put in here??
}
Thanks very much for any insight! I need it!!
This is something that is much easier to do using data binding. You can bind the TextBlock.Text property directly to the ListBox using an ElementName binding:
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedItem.id}" />
Alternatively, if you set set SelectedValuePath="id" on the ListBox, then binding to SelectedValue will give you the "id" property:
<ListBox x:Name="listBox1" SelectedValuePath="id" ... />
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedValue}" />
As a side note (as #Rachel already noted in comments): you may as well just set the ItemsSource, rather than looping through and adding each manually. All you need is this:
listBox1.ItemsSource = feed2;
Edit
Ok, if you wanted to use the procedural approach, here's how you would do it. (No one would recommend this approach, especially if you're learning/teaching. Try to make full use of data binding, and view-viewmodel separation.)
void PrintText(object sender, SelectionChangedEventArgs args)
{
var listBox = (ListBox)sender;
RootObject selectedItem = listBox.SelectedItem;
someTextBox.Text = selectedItem.id;
}
If all you want to do is click an item in the ListBox and get it to show up in the TextBox, you don't need fancy binding (in that other answer) to do it. You can simply add a MouseUp event in the ListBox XAML:
MouseUp="ListBox1_MouseUp"
This would work similar to the SelectionChanged event you wanted to use.
You then right-click that function name in the XAML page and select "Go to definition". It will create the next function for you:
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
}
Simply add in there to update the TextBox you want with the SelectedItem values from sender:
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
ListBox lstBox = (ListBox)sender;
ListBoxItem item = lstBox.SelectedItem;
if (item != null) // avoids exception when an empty line is clicked
{
someBox.Text = item.name;
someOtherBox.Text = item.id;
}
}
I later found that blockNameList and blockIdList are not accessible via intellisense because they are within the DataTemplate of the ListBox, so I put someBox and someOtherBox, as references to other TextBoxes you would have to add to the XAML, outside of the ListBox. You would not re-write data inside the ListBox on the same item by clicking it. Even if you could reach the template's TextBlock to do it, you'd just be re-writing that same item with its own values, since it would be the SelectedItem!
Even though there are those that don't recommend this approach because they like binding everything - and in some cases you want binding to occur so that controls on the page update as a result of dependencies (i.e. do one thing to cause another), I find that manual methods of clicking a button/item/control to update something are just fine and avoid all the model/MVVM BS that has taken over WPF and over-complicated it.

How to bind an object to a ComboBox, but update a different object when it changes?

I have a Zone object that contains
public int Block {get;set;}
I also have a configuration object which contains minimum and maximum Block values, which are 0 and 2 respectively.
I need to display a ComboBox with the range of valid values, but I need to have the selected value bound to Block.
What's the best way for me to do this?
I've been trying the following:
var blocks = new Dictionary<string, int>();
for (int i = _currentZone.Constraints.Block.Min; i <= _currentZone.Constraints.Block.Max; i++)
{
blocks.Add("Block " + i, i);
}
var blocksCombo = new ComboBoxControl(blocks, GetCurrentBlockValue());
with ComboBoxControl defined as
public ComboBoxControl(Dictionary<string, int> comboItems, int? selectedValue)
{
InitializeComponent();
cboItems.ItemsSource = comboItems;
cboItems.SelectedValue = selectedValue;
}
and the XAML defined as
<Grid>
<ComboBox x:Name="cboItems"
SelectionChanged="combo_SelectionChanged"
Height="25"
SelectedValuePath="Value">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
When the combo_SelectionChanged event is triggered I manually update the Block value, which isn't ideal.
What I'd like is to be able to set the combo box with the items in the dictionary, but when I change the selected item the value is bound to a different object - the Block. Is this possible?
If so, how can I implement this? If not, is there a better way for me to go about this than what I'm currently doing?
I believe it's as simple as changing you xaml to have...
<ComboBox x:Name="cboItems"
SelectionChanged="combo_SelectionChanged"
Height="25"
SelectedValuePath="Value"
SelectedItem="{Binding Block}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Assuming the data context is setup correctly, you probably need to set the datacontext of the combobox to your Zone object at some point, maybe pass it along with the constructor...
var blocksCombo = new ComboBoxControl(blocks, GetCurrentBlockValue(), this);
public ComboBoxControl(Dictionary<string, int> comboItems, int? selectedValue, Zone zone)
{
InitializeComponent();
cboItems.ItemsSource = comboItems;
cboItems.SelectedValue = selectedValue;
cboItems.DataContext = zone;
}
edit:
Also I think Henk is right, you might want to change the dictionary to instead be a ObservableCollection of Block. (actually just realized block is just an int, this will probably work as a dictionary)
I hope I understood everything right. You have the combobox and want to bind to one specific zone?
<ComboBox ItemsSource="{Binding ValidValuesList}" ItemStringFormat="Block {0}" SelectedItem="{Binding MyZone.Block}"/>
This binds to
public List<int> ValidValuesList
{
get { return new List<int> { 0, 1, 2 }; }
}
and to
public Zone MyZone { get; set; }
in your usercontrols DataContext.

Categories