I am fairly new to WPF and MVVM and a newb in general, so thank you in advance for your patience.
I am using a custom class in my model, and I have an ObservableCollection of that custom object in my viewmodel. In the class' constructor, I am adding the object to the collection when it is instantiated. In my view, I am using a DataGrid that is bound to the collection to list all active instances of the class. I am trying to implement a drag-and-drop from the DataGrid onto a trash can icon that would allow a user to dispose of unneeded instances of the class.
The problem is that when you click anything in the DataGrid, the program immediately crashes with an ArgumentOutOfRange exception - ("The given DisplayIndex is out of range. DisplayIndex must be greater than or equal to 0 and less than Columns.Count." "Actual value was 0"). DisplayIndex seems to relate to the DataGrid column, so this exception is probably due to the fact that I am not displaying any columns in the traditional sense - in my DataGrid, AutoGenerateColumns is set to False, and I am displaying everything I need to display using a RowDetailsTemplate. (The reason for this is that the area where I am displaying the DataGrid is narrow, so I need a nested, item-specific grid to represent the item properly.) The DataGrid displays and syncs with the collection fine, but obviously has some issues. I have read dozens of links on DataGrid crashes, and haven't found anything involving this exception.
My desired behavior is to pass the custom object represented by the DataGrid item to a target when I drag and drop it. I don't care which "column" they clicked or anything else - I just need a way to pass either an object reference or a SelectedIndex (the items index in the collection) to a method in the viewmodel.
Thank you in advance for any help! The offending bit of code (XAML) seems to be:
<ScrollViewer DockPanel.Dock="Bottom" Margin="2" Width="180" ScrollViewer.VerticalScrollBarVisibility="Auto">
<DataGrid ItemsSource="{Binding Path=myCollection, Mode=OneWay}" AutoGenerateColumns="False" RowDetailsVisibilityMode="Visible" HeadersVisibility="None">
<DataGrid.RowDetailsTemplate>
<DataTemplate DataType="model:myClass">
<Border CornerRadius="10" Background="AliceBlue">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding MyString1}" FontSize="21" VerticalAlignment="Bottom" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding MyCustomProperty, Converter={StaticResource MyIValueConverter}}" VerticalAlignment="Bottom" />
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding MyString2}" TextWrapping="Wrap" />
<Image Source="/Resources/image1.png" Grid.Column="2" Grid.Row="0">
<Image.DataContext>
<Properties:Resources/>
</Image.DataContext>
</Image>
<Image Source="/Resources/image2.png" Grid.Column="2" Grid.Row="1">
<Image.DataContext>
<Properties:Resources/>
</Image.DataContext>
</Image>
</Grid>
</Border>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</ScrollViewer>
The issue was indeed because I am not generating any "traditional" columns. This is apparently a known bug, documented here: Microsoft Bug Report
As a workaround, I just defined an invisible column within the DataGrid and it seems to behave properly now:
<DataGrid.Columns>
<DataGridTemplateColumn Visibility="Hidden" />
</DataGrid.Columns>
Related
the issue is hard to explain but I will try my best. This code is part of a proof-of-concept for a webscraping application I am working on - only recently started.
I am attempting to display 2 columns of data using a DataGrid. I am able to display the data by assigning an ObservableCollection<> to the DataGrid.ItemsSource(see image below), the issue I am getting is that I have no control over column properties. I am very new to WPF and have tried to 'sort of' adopt MVVM.
In the picture it shows 4 columns, both the 'Title' and 'Price' from the ItemsSource are the 2 large ones.
private void ScrapeProductButton_OnClick(object sender, RoutedEventArgs e)
{
string url = this.ProductToScrapeUrlTextBox.Text;
scraper.ScrapeData(url);
var entries = scraper.Entries;
WebScrapedItems.ItemsSource = entries;
WebScrapedItems.Columns.Add(new DataGridTextColumn(){Header = "Title",
Width = new DataGridLength(0.8, DataGridLengthUnitType.Star)});
WebScrapedItems.Columns.Add(new DataGridTextColumn(){Header = "Price",
Width = new DataGridLength(0.2, DataGridLengthUnitType.Star)});
}
As seen in the code behind above, I tried to add 2 columns of the correct formatting assuming the contents of the ItemsSource would just do the same (obviously it isn't). Turning AutoGeneratedColumns off in the XAML doesn't display the ItemsSource but does display the other columns correctly.
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Button Name="ScrapeProductButton" Grid.Column="0"
Margin="10 10 10 10" Background="AntiqueWhite"
Content="Scrape Website" FontWeight="Bold"
Click="ScrapeProductButton_OnClick"/>
<TextBox Name="ProductToScrapeUrlTextBox" Grid.Column="1"
Margin="10" Padding="2"/>
</Grid>
<DataGrid Height="350" Width="740" Margin="10" Name="WebScrapedItems"
VerticalAlignment="Center" HorizontalAlignment="Center"
RowHeight="30" ColumnWidth="390" IsReadOnly="True"
AutoGenerateColumns="True" FrozenColumnCount="2">
</DataGrid>
</StackPanel>
My goal is to just display the contents of the ItemsSource objects but such that they are correctly formatted. I feel like binding could be a potential reason its not working properly, but again I am still new to WPF and haven't started reading up on it yet.
Any help would be greatly appreciated, and any WPF advice, MVVM or anything would be great also.
Thanks!
I fixed the issue through binding to the field of the object that I wanted to display within the column. This mean I could remove the formatting the in ScrapeProductButton_OnClick method.
XAML code is here:
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Button Name="ScrapeProductButton" Grid.Column="0" Margin="10 10 10 10" Background="AntiqueWhite" Content="Scrape Website" FontWeight="Bold" Click="ScrapeProductButton_OnClick"/>
<TextBox Name="ProductToScrapeUrlTextBox" Grid.Column="1" Margin="10" Padding="2"/>
</Grid>
<DataGrid Name="WebScrapedItems" AutoGenerateColumns="False">
<DataGrid.Columns >
<DataGridTextColumn Header="Title" Binding="{Binding Title}" Width="3*"/>
<DataGridTextColumn Header="Price" Binding="{Binding Price}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
I appreciate the time people spent on looking through the problem - sorry if I wasted your time.
I'm experiencing some strange behaviour with the AutoSuggestBox in UWP platform, especially regarding its suggestion list elements.
I want to customize those elements with an hover effect that makes a Button appear when the pointer enters the item template… and I would like also to stretch the item's content through all the available template width.
This is the code for the AutoSuggestBox (sorry for some non-english property name, but I think it isn't important):
<AutoSuggestBox MinWidth="300" Text="{x:Bind ViewModel.TestoRicerca, Mode=TwoWay}" ItemsSource="{x:Bind ViewModel.ListaRicerca, Mode=OneWay}" QueryIcon="Find">
<AutoSuggestBox.ItemTemplate>
<DataTemplate x:DataType="vm:Colore">
<Grid PointerExited="{x:Bind OnExitPointer}" PointerEntered="{x:Bind OnEnterPointer}" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Rectangle Width="30" Height="30" Margin="0,5,15,5" Fill="{x:Bind ColorBrush, Mode=OneWay}" Stroke="LightGray" StrokeThickness="1" RadiusX="2" RadiusY="2" VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<TextBlock Text="{x:Bind Nome, Mode=OneWay}" VerticalAlignment="Center" FontWeight="Bold" TextWrapping="Wrap"/>
<TextBlock Text="{x:Bind ColorAsString, Mode=OneWay}" Foreground="LightGray" TextWrapping="Wrap" Margin="0,2,0,0"/>
</StackPanel>
<Button Grid.Column="2" Content="Click here" Visibility="{x:Bind IsHovered, Mode=OneWay}" VerticalAlignment="Center" Click="{x:Bind DisplayDetail}"/>
</Grid>
</DataTemplate>
</AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>
The OnEnterPointer and OnExitPointer functions, placed in the main Grid of the ItemTemplate, should make the Button appear always when the pointer is into the Grid area… but this doesn't happen!
Actually, the OnEnterPointer fires ONLY when the pointer is over the Grid's children!
This is shown here in those photos:
How could I make the hover effect firing in the entire Grid's area?
THe other thing is about stretching the list item's content, as I said before and as you can see in the pictures… in a normal ListView I know how to do it (place a custom Style in ListView.ItemPresenterStyle with the properly Setter), but here it doesn't work somehow, maybe because it isn't a ListViewItem the standard item in that suggestion list…
If you have any idea about this question also, I would like to know it.
I thank you for your attention and your patience.
Best regards
I have a situation where I am trying to set the tab order (tabindex) for controls that are loaded dynamically. The main XAML looks like this:
<ItemsControl DockPanel.Dock="Top" ItemsSource="{Binding ItemsDataSource}" Name="overlayItems" ItemTemplate="{StaticResource DetailsTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
For example purposes, assume the DetailsTemplate is something simple like this:
<DataTemplate x:Key="DetailsTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition Height="22" />
<RowDefinition Height="22" />
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Padding="0">Field 1</Label>
<TextBox Grid.Column="1" Grid.Row="0" Name="field1TextBox" TabIndex="0" Text="{Binding Field1Value}"/>
<Label Grid.Column="0" Grid.Row="1" Padding="0">Field 2</Label>
<TextBox Grid.Column="1" Grid.Row="1" Name="field2TextBox" TabIndex="1" Text="{Binding Field2Value}"/>
<Label Grid.Column="0" Grid.Row="2" Padding="0">Field 3</Label>
<TextBox Grid.Column="1" Grid.Row="2" Name="field3TextBox" TabIndex="2" Text="{Binding Field3Value}"/>
</Grid>
</DataTemplate>
This XAML works just fine except for the resulting tab order.
Assuming that the ItemsDataSource is a collection of a class and contains 3 instances of that class, three sets of the DetailsTemplate data template are created. However the tab order does not change, every field1TextBox remains at TabIndex 0. This means, instead of tabbing from the first instances of field1TextBox, to field2TextBox, to field3TextBox, the tab goes from the first instance of field1TextBox to the second instance of field1TextBox then to the third instance of field1TextBox then to the first instance of field2TextBox, and so on. My question is, how do I get the tab order corrected where, say, the second instance of the data template would have its text boxes tab indexes updated to 3, 4 and 5 respectively?
You'll find the answer in the KeyboardNavigation.TabNavigation Attached Property page from MSDN. This property Gets or sets the logical tab navigation behavior for the children of the element that this property is set on.
There are several possible values in the KeyboardNavigationMode Enumeration used that affect the tabbing order in different ways, but you're after the Local value, which has the effect that Tab Indexes are considered on local subtree only inside this container and ... [Navigation leaves the containing element when an edge is reached].
<Grid KeyboardNavigation.TabNavigation="Local">
...
</Grid>
I want to create a debug window that will allow us to edit properties from various objects in our app while it runs. This will allow us, for example, to tweak threshold values for certain heuristic rules in our app, without requiring a rebuild and/or app restart.
The goal is to tell the debug window to enable editing some property of an object. The window then obtains the property's value, keeps a weak reference to the object and displays an appropriate data template (based on the value's type) to enable us to edit the value and apply the new value to the object when needed.
Problem:
The data templates are applied correctly and the value is displayed for each debug item in a TextBox. However, the Value property of each DebugItem, to which the TextBox is bound, is NEVER updated. I have set a breakpoint on that property's setter; the breakpoint is never triggered.
Here is my current setup:
I have a DebugItems collection of DebugItem objects in my view model.
Each DebugItem has a Value property of type object.
For debugging purposes, the Value property always contains a string.
I have created a data template for the DebugItem type and the System:String type.
My window contains a ListBox that is bound to the DebugItems collection and displays DebugItems using the data template defined above, in a ContentPresenter. A TextBox inside that data template is also bound to the Value so that it enables us to edit the string value using the other, System:String data template defined above *.
* I am under the impression that this has to do with why the edit doesn't work. I could be mistaken, through.
Relevant part of the window:
<Grid Background="#CECECE">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding DebugItems}" Background="Transparent" BorderBrush="Transparent" />
</ScrollViewer>
</Grid>
My data template:
(Of particular interest are the inner ContentPresenter and its embedded System:String data template.)
<DataTemplate DataType="{x:Type Debug:DebugItem}">
<Grid Height="60" d:DesignWidth="403.06">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" />
<ColumnDefinition Width="181*" />
<ColumnDefinition Width="110" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Label}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="14,0,0,0" Foreground="Black" FontWeight="Bold" />
<ContentPresenter VerticalAlignment="Center" Grid.Column="1" Content="{Binding Value}" Height="Auto">
<ContentPresenter.Resources>
<!-- String -->
<DataTemplate DataType="{x:Type System:String}">
<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
<UniformGrid Grid.Column="2" Rows="1">
<Button Margin="8,8,0.5,8" Command="{Binding UpdateCommand}" Style="{DynamicResource ButtonStyle}" Content="Update" />
<Button Margin="4.5,8,8,8" Command="{Binding ApplyCommand}" Style="{DynamicResource ButtonStyle}" Content="Apply" />
</UniformGrid>
</Grid>
</Grid>
</DataTemplate>
Any ideas?
I have a page that has a listbox with the following item template as follows:
<ListBox x:Name="test">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid MaxHeight="108" Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle Height="108" Grid.Column="0" Fill="{Binding Color}"/>
<Image Source="{Binding Image}" Height="108" Width="108" Grid.Column="1" HorizontalAlignment="Left" Stretch="UniformToFill"/>
<StackPanel Grid.Column="2">
<TextBlock Text="{Binding Title}" TextWrapping="NoWrap" />
<TextBlock Text="{Binding SubHeading}" TextWrapping="NoWrap" />
<TextBlock Text="{Binding Body}" TextWrapping="Wrap" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
On the OnNavigatedTo event of the page, I set the item source of the list box to an observable collection of about 20 items.
All is well and the list is populated, however when I scroll up or down the list, the items appear to look out of sync on the UI. e.g the text that was shown on the first list item appears on the last item in the list box, sometimes there's duplicates, and everytime you swipe up or down the items are different.
I have debugged the listbox items, and I can see that correct objects are being bound to the right items. So it's only what is shown on the UI that is incorrect.
I have also tried explicitly using the standard stackpanel as opposed to a virtualizationstackpanel and that works around the issue by making sure all items are loaded in memory.
I don't believe that removing virtualization is the answer. There must be a root cause. However it may be acceptable as my listbox will never really contain more than 30 items.
On another page, I do the same thing with the silverlight toolkit longlistselector, and have the same issue. However I am not sure of how to remove virtualization on a longlistselector.
So to summarize, what might be the underlying issue that causes the listbox items to not update the UI properly when scrolling? If removing virtualization is the only answer, how may I do this on the longlistselector?
Thanks for anyhelp on this.
Maybe it is some sort this problem? (jumpy ListBox while scrolling)
As it turns out, it was a binding issue, and nothing to to with the listbox whatsoever.
I was (unknowingly) removing the binding of some properties in the item template, after I had bound to them (in some other bit of code) Therefore every time the listbox recycled the containers for new items, it could not update it with the correct info.
Thanks all for your help