App closes when item deleted from long list selector - c#

I've just made a simple app with a longlistseletor, which include an item and an button to delete this item. I think nothing is wrong but each time i delete 4 or 5 items from this list, app always close. Is it because of lack of RAM ?
btw, my english is bad, sorry :P
XAML code:
<phone:LongListSelector ItemsSource="{Binding ItemsIdea}">
<!-- item quick idea template-->
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid HorizontalAlignment="Stretch" Margin="0,20,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Fill="{StaticResource PhoneAccentBrush}"/>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding IdeaContent}" TextWrapping="Wrap" Style="{StaticResource PhoneTextLargeStyle}"/>
</StackPanel>
<Button Grid.Column="2" Click="btDeleteIdea_Click" BorderThickness="0.0" VerticalAlignment="Top">
<Image Source="/Assets/Icons/cancel_small.png"/>
</Button>
</Grid>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
and the C# code:
private void btDeleteIdea_Click(object sender, RoutedEventArgs e) {
var button = (sender as Button).DataContext as IdeaViewModel;
if (button != null)
App.ViewModel.DeleteIdea(button);
}

The problem may be connected with some little bugs in LongListSelector (I'm not aware if they still exist in current version and the version you use).
Here you can find similar problem and the solution is to extend your LLS class. I had similar problem some time ago and this helped:
public class LongListSelectorExtension : LongListSelector
{
protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
{
try { return base.MeasureOverride(availableSize); }
catch (ArgumentException) { return this.DesiredSize; }
}
}
You can also look here where are some improvements to LLS code.
Maybe this will help.
To use your extension you will have to modify your XAML:
Add namespace of your project where the class is at the beginning where other xmlns are:
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:local="clr-namespace:Example"
Then you should be able to use your class in XAML file instead normal LLS:
<local:LongListSelectorExtension .../>

Related

ListView that have its .itemssource set to a list but doesn't update when I add item to the list

It sounds quite simple, but actually took me several hours and still not done.
Here is my C# code:
public ItemEditor() {
InitializeComponent();
ListView_Item.ItemsSource = TestList;
}
List<Item> TestList = new List<Item>();
private void MenuItem_AddNewItem_Click(object sender, RoutedEventArgs e) {
Item newItem = new Item();
TestList.Add(newItem);
newItem.Name = "new item";
}
When function MenuItem_AddNewItem_Click is executed, the listview never update. I cannot figure out how can it be like this.
And here is my xaml code:
<Grid DockPanel.Dock="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListView x:Name="ListView_Item" Grid.Column="0" ContextMenu="{StaticResource ContextMenu_ListView_Item}" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<GridSplitter Grid.Column="0" Width="5"/>
<StackPanel Grid.Column="1" Orientation="Vertical">
<DockPanel>
<TextBlock>Name:</TextBlock>
<TextBox></TextBox>
</DockPanel>
</StackPanel>
<GridSplitter Grid.Column="1" Width="5"/>
<Grid Grid.Column="2"></Grid>
</Grid>
Can any one help me? I have looked at many webpages, there were similar situations, but non of them can help me solve my problem.
As mentioned in the comments to the question, using an ObservableCollection is the simplest way to achieve this. It's what that collection is designed for. If you ever need to modify an existing custom collection type rather than using ObservableCollection you'd want to implement INotifyPropertyChanged and INotifyCollectionChanged.

UWP VisualTreeHelper.GetParent() returns null

I have a ContentDialog which has a ListView. This ListView's DataTemplate Contains a Grid and this Grid has a Button. The code goes like this:
<ContentDialog x:Name="DownloadListDialog" x:FieldModifier="public" Grid.Column="1">
<ListView Name="AssetsListView" IsItemClickEnabled="False" Grid.Row="1" SelectionMode="Single" MaxHeight="500" ItemsSource="{x:Bind _viewModel.Assets, Mode=OneWay}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
...
...
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewModel:AssetViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Text="{x:Bind name}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind lblFileSize}"/>
<TextBlock Text="{x:Bind contentSize, Mode=OneWay}"/>
<TextBlock Text="{x:Bind contentUrl}" Visibility="Collapsed"/>
</StackPanel>
</StackPanel>
<Button Content="Download" Click="Button_Click" HorizontalAlignment="Right" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentDialog>
Here's my Button Click event handler:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var grid = VisualTreeHelper.GetParent(sender as Button) as Grid;
...
...
}
The problem is that the variable VisualTreeHelper.GetParent(sender as Button) as Grid always returns null on my PC. But this same code when I deploy on my mobile works perfectly fine (i.e, variable grid gets assigned the correct value).
UPDATE:
Here's my Live Visual Tree and it confirms that the button has a parent grid.
App min version: build 14393
App target version: Build 15063
PC windows version: Build 17134 (version 1803)
Note: I've tried changing the App target version to 1803 but the problem remains.
As I understand from a different question there are several ways to get the parent of the VisualTreeHelper. Could it be that on your mobile or PC for that matter in the background different things are loaded so that the location of where you can find the grid object changes.
You could check this answer as a reference of what I stated above: FrameworkElement.Parent and VisualtreeHelper.GetParent behaves differently

ListView Changing Source Value Keeps the Selected Item Active Unless I revert my changes

So I have a ListView bound to a Client Object. The client properties are shown on some textbox next to the ListView. If I change the name (that is shown in the ListView), I can't select anything else unless I revert back the name.
The ListView SelectedItem always stays selected if I change the value in the Text Box and the Text Box all show the same values, even if I select another.
I tried clearing the selected item, the ItemsSource nothing will work. It must be something with the source and the bindings.
My Source is a ObservableDictionary that I found somewhere here, so maybe that is the issue. My Client Object is Also implementing INotifyPropertyChanged. I tried a lot of thing with Binding and etc. But whatever I try the text Box will stay like that even if I select something else as long has the value isn't the restored to it's Original Value I can't select anything else.
XAML
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<ListView x:Name="listView" Grid.RowSpan="7" Grid.Row="1" Margin="5,0" SelectionMode="Single" IsSynchronizedWithCurrentItem="True">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ClientIDandName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Text="ID" Grid.Row="1" Grid.Column="1"/>
<TextBox Text="{Binding SelectedItem.ClientID, ElementName=listView}" IsEnabled="False" Grid.Row="1" Grid.Column="2"/>
<TextBlock Text="Nom" Grid.Row="2" Grid.Column="1"/>
<TextBox Text="{Binding SelectedItem.ClientName, ElementName=listView}" Grid.Row="2" Grid.Column="2"/>
<TextBlock Text="Temps de Transit" Grid.Row="3" Grid.Column="1"/>
<TextBox Text="{Binding SelectedItem.TransitTime, ElementName=listView}" Grid.Row="3" Grid.Column="2"/>
<TextBlock Text="Nbr. de Jour Max" Grid.Row="4" Grid.Column="1"/>
<TextBox Text="{Binding SelectedItem.MaxShipDays, ElementName=listView}" Grid.Row="4" Grid.Column="2"/>
<TextBlock Text="Exclure" Grid.Row="5" Grid.Column="1"/>
<CheckBox IsChecked="{Binding SelectedItem.IsExcluded, ElementName=listView}" Grid.Row="5" Grid.Column="2" VerticalAlignment="Bottom"/>
<Button x:Name="btnSave" Content="Sauvegarder" Click="btnSave_Click" Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Center"/>
</Grid>
Code Behind
public partial class ClientConfig : UserControl
{
public ClientConfig()
{
InitializeComponent();
listView.ItemsSource = Client.LocalDB.Values.ToObservableCollection();
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
Client client = listView.SelectedItem as Client;
if (client != null)
{
client.Save();
}
}
}
I usually use the MVVM design pattern and keep my objects in a viewmodel, so you will have to modify the code accordingly.
I think that your problem is how you create your ObservableCollection in code. When you call ToObservableCollection(), it enumerates all of the values in Client.LocalDB.Values, but I don't think it will persist as an object like you expect it to - especially since it is local to the method that you are creating it in. The ListView continues to show data because nothing is telling it to change, but the ObservableCollection does not actually exist anymore. You will want to create an observable collection that is global to the whole class, and populate it instead.
This is untested code, but should get you close... in code behind:
private ObservableCollection<Whatever> _myCollection;
public ObservableCollection<Whatever> MyCollection
{
get
{
return _myCollection;
}
set
{
if (_myCollection != value)
{
_myCollection = value;
RaisePropertyChanged("MyCollection");
}
}
}
private Whatever _selectedWhatever;
public Whatever SelectedWhatever
{
get
{
return _selectedWhatever;
}
set
{
if (_selectedWhatever != value)
{
_selectedWhatever = value;
RaisePropertyChanged("SelectedWhatever");
}
}
}
public ClientConfig()
{
InitializeComponent();
foreach (Whatever whatevs in Client.LocalDB.Values.ToObservableCollection())
{
MyCollection.Add(whatevs); // populate the collection this way and it will persist
}
}
and in XAML:
<ListView x:Name="listView" Grid.RowSpan="7" Grid.Row="1" Margin="5,0" SelectionMode="Single" IsSynchronizedWithCurrentItem="True" ItemSource="{Binding MyCollection}" SelectedItem="{Binding SelectedWhatever}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ClientIDandName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Notice that you have now bound the collection to a persistent variable that can be changed, and you have bound the selected item to a specific instance of that collection. Now you can bind your textboxes to the SelectedWhatever and changes made in one place should be reflected in the other place.

Proper Code-Behind Object Data Binding in XAML?

I am currently binding an object in my code-behind (C#) to my XAML by giving a name to the XAML control, and setting the DataContext in the code-behind.
public partial class SmsControl: UserControl
{
private readonly DataOrganizer _dataOrganizer;
public FunctionalTester _funcTester;
public SmsControl()
{
InitializeComponent();
_dataOrganizer = new DataOrganizer();
_funcTester = new FunctionalTester();
// Set the datacontext appropriately
grpModemInitialization.DataContext = _funcTester;
}
private async void Button_Click_2(object sender, RoutedEventArgs e)
{
await _funcTester.Test();
}
}
And my XAML...
<!-- SMS Test Modem Initialization GroupBox -->
<GroupBox x:Name="grpModemInitialization" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="3" Style="{StaticResource groupboxViewItem}">
<GroupBox.Header>
<Label Content="SMS Test Modem Initialization" Style="{StaticResource boldHeaderItem}"/>
</GroupBox.Header>
<!-- SMS Test Modem Initialization Grid -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Content="COM:" Style="{StaticResource boldHeaderItem}" />
<ComboBox Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" Style="{StaticResource comboBoxItem}" ItemsSource="{Binding AvailableCommPorts}" SelectedItem="{Binding SelectedCommPort}" />
<Label Grid.Column="2" Grid.Row="0" Content="Modem Ready:" Style="{StaticResource boldHeaderItem}" />
<Label Grid.Column="2" Grid.Row="1" Content="RSSI:" Style="{StaticResource boldHeaderItem}" />
<Label Content="{Binding ModemReady}" Grid.Column="3" Grid.Row="0" HorizontalContentAlignment="Left" VerticalAlignment="Center"/>
<Label Content="{Binding ModemRssi}" Grid.Column="3" Grid.Row="1" HorizontalContentAlignment="Left" VerticalAlignment="Center" />
<Label Grid.Column="4" Grid.Row="0" Content="Modem #:" Style="{StaticResource boldHeaderItem}"/>
<Button Grid.Column="4" Grid.Row="1" Grid.ColumnSpan="2" Content="Initialize" />
<Label Content="{Binding ModemNumber}" Grid.Column="5" Grid.Row="0" HorizontalContentAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</GroupBox>
The code above works fine - no problems. What I'm asking is, if there is a way to set the DataContext of the GroupBox in XAML, referencing my _funcTester object, instead of setting the DataContext in the code-behind? The reason I ask, is because different controls need to be bound to different objects in the code-behind and I'm not finding good resources on how to do so, except as I show above (giving a "x:Name" to each XAML control and setting the DataContext in code-behind). Any help is appreciated! Thanks!
You don't want to reference UI elements by name in the code-behind. Actually any time you can avoid naming an object you save a little in performance. And by setting up your app to use MVVM properly, you gain in performance, readability, maintainability, and code separation.
You want to abstract things further to use the MVVM pattern. You're doing your bindings correctly but consider the pattern. Your view is all correct. Consider adding a class that holds the properties defined currently in your code-behind and the methods called in your event handlers.
public class ViewModel : INotifyPropertyChanged
{
private FunctionalTester _funcTester;
public FunctionalTester FuncTester
{
get
{
return _funcTester;
}
set
{
_funcTester = value;
OnPropertyChanged( "FuncTester" );
}
}
public async void TestAsync( )
{
await _funcTester.Test( );
}
}
A binding to the FuncTester would simply be SomeProperty="{Binding FuncTester}" because the object is set as the DataContext of your view. A decent article that expands on this idea is found here.
Obviously left out some things (like INotifyPropertyChanged implementation and other properties you've defined) for brevity. But just make this class and assign it as your view model. Then the UI (the Xaml and the code-behind) only really deal with the UI and the ViewModel really deals with the data and logic. Great separation. For your event handler, just call ((ViewModel)this.DataContext).Test( ); and you can plug-and-play your ViewModel's to change functionality on the fly. Hope this helps!
Just set the DataContext of whole UserControl to self i.e do
this.DataContext = this; in constructor.
Then define the Property for _functinTester
public FunctionalTester FuncTester { get {return _funcTester} };
Now in your xaml you can do
<GroupBox x:Name="grpModemInitialization" DataContext="{Binding FuncTester}"/>
In this way since you have the DataContext set for your whole usercontrol, you can bind any control to any property within that DataContext

Popup does not closed event Stayopen set as False

I have use the following code snippet to define the Popup.
Code snippet[XAML]:
<Grid Margin="0,0,0,0" Height="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="19" />
<ColumnDefinition Width="19" />
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Stretch"
Text="HeaderText"
FontWeight="Bold"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"/>
<Grid Grid.Column="1">
<Button Width="19" x:Name="FilterButton" Click="FilterButton_OnClick" Content="^"/>
<Popup x:Name="FilterPanel" StaysOpen="False" >
<Border >
<Grid>
<TextBlock x:Name="tblTitle" Text="PopUp Header" Background="Red" Grid.Column="0" Grid.Row="0"/>
</Grid>
</Border>
</Popup>
</Grid>
<TextBox Text="Test" Grid.Column="2"/>
</Grid>
I have use the following code snippet to open the popup
Code snippet[C#]:
private void FilterButton_OnClick(object sender, RoutedEventArgs e)
{
this.FilterPanel.IsOpen = true;
}
Scenorio:
Open the popup using button click.
Press Tab.
Focus move to TextBox.
Actual Behavvior:
Popup does not close.
Expected Behavior:
Popup should be closed.
For your reference here I have attached the simple sample .Can you please any look into this and provide guidance to archive my requirement. Thanks in advance.
This is very strange. I took your code and pasted it in a new project window and popup closed every time. However I remember when I wanted to create my own custom control I had a similar issue. I know one of the things is to set StaysOpen to false. This I see you have already done. Another is try to setting the following when FilterPanel is initialized
FilterPanel.IsMouseCaptureWithinChanged +=FilterPanel_IsMouseCaptureWithinChanged;
void FilterPanel_IsMouseCaptureWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (!(bool)e.NewValue)
{ FilterPanel.IsOpen = false; }
}
As I said my code worked perfectly when I copied your code, so I couldn't test it, but the above should work.

Categories