why are the converters being called again? - c#

In my windows phone app one of the pages contains LongListSelector. In that LongListSelector I am using some converters. When the LongListSelector Loaded the Converters are being called as usual. But when I call ScrollTo() method to scroll to a specific item of the LongListSelector, the converters are being called again. Why are the converters being called again? What does ScrollTo() method do that causes converters to be called again?
Sample Code:
XAML:
<phone:LongListSelector ItemTemplate="{StaticResource LLSItemSource}"
Name="ChatListBox">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent"
Visibility="{Binding ID, Converter={StaticResource visibilityConverter}}"
Margin="0,6">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="delete"
Click="ContextMenuItem_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<Border Background="Black"
HorizontalAlignment="Center"
Padding="30,5,30,8"
CornerRadius="20">
<TextBlock Text="{Binding Date, Converter={StaticResource dateStringConverter}}"
Foreground="White"
FontSize="20" />
</Border>
</Grid>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
CS:
private void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
string header = (sender as MenuItem).Header.ToString();
MessageModel selectedListBoxItem = (sender as MenuItem).DataContext as MessageModel;
if (selectedListBoxItem == null)
return;
if (header == "delete")
{
DeleteItemByID(selectedListBoxItem.ID);
if (ChatListBox.ItemsSource.Count > 0)
{
ChatListBox.ScrollTo(ChatListBox.ItemsSource[delIndex - 1]); // here I am scrolling to the last item that causes converters to be called again
}
}
}

You are written converter for longlistselector items. On loading every item in selector its get executing to convert value on given template

Related

How to get the destination ListView item on drop?

In UWP C#, I have one ListView in upper row & another in lower row. When I drag a listitem from upper ListView & drop it on lower ListView, I am getting the source. But, I am unable to get the destination. ie) the listview item/(Folder object in my case) where I dropped.
<ListView Name="ListviewCars"
CanDragItems="True" DragItemsStarting="ListviewCars_DragItemsStarting"
SelectionMode="Single" IsItemClickEnabled="True"
DataContext="Cars" ItemsSource="{Binding CarsCollection}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="Transparent" Height="80" Orientation="Horizontal"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate>
<Grid Name="GrdCars" >
<Grid Height="80" Width="90" Padding="5">
<Grid.Background>
<ImageBrush Stretch="Uniform" ImageSource="Assets/car.png"/>
</Grid.Background>
<TextBlock Text="{Binding Name}" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Name="GrdViewImg" ScrollViewer.VerticalScrollBarVisibility="Disabled"
AllowDrop="True" DragOver="Image_DragOver"
Drop="Image_OnDrop"
DataContext="Folders" ItemsSource="{Binding FolderCollection}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="Transparent" Height="80" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate>
<Grid Name="GrdForFolderMenu" RightTapped="GrdForFolderMenu_RightTapped">
<Grid Height="80" Width="90" Padding="5">
<Grid.Background>
<ImageBrush Stretch="UniformToFill" ImageSource="Assets/Folderimage.png"/>
</Grid.Background>
<TextBlock Text="{Binding Name}" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have found a simple solution myself. I get the source from DragItemsStarting event of the SoureListView. and target item (as mentioned by ashchuk) from a Grid placed inside Datatemplate of Target ListView. as shown below. Everything works fine now!
(Cars is my Source custom list item. Folders is my Target custom list item)
private void SoureListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
Cars x = e.Items[0] as Cars;
string DraggedSourceCar = x.Name;
e.Data.Properties.Add("myArgs", DraggedSourceCar);
}
private void GridInsideDatatemplateOfTargetListview_Drop(object sender, DragEventArgs e)
{
var x = sender as Grid;
var y = x.DataContext as Folders;
string toMoveFolderName = y.Name;
string DraggedSourceCar = e.DataView.Properties["myArgs"].ToString();
Debug.WriteLine(DraggedSourceCar + Environment.NewLine + toMoveFolderName );
}
private void TargetListview_DragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Copy;
}
You have to find out by yourself = DragEventArgs.GetPosition() in the destination drop, then the underlying item with smoe helper functions
public static object GetObjectAtPoint<ItemContainer>(this ItemsControl control, Point p)
where ItemContainer : DependencyObject
{
// ItemContainer - can be ListViewItem, or TreeViewItem and so on(depends on control)
ItemContainer obj = GetContainerAtPoint<ItemContainer>(control, p);
if (obj == null)
return null;
return control.ItemContainerGenerator.ItemFromContainer(obj);
}
public static ItemContainer GetContainerAtPoint<ItemContainer>(this ItemsControl control, Point p)
where ItemContainer : DependencyObject
{
HitTestResult result = VisualTreeHelper.HitTest(control, p);
if (result != null)
{
DependencyObject obj = result.VisualHit;
while (VisualTreeHelper.GetParent(obj) != null && !(obj is ItemContainer))
{
obj = VisualTreeHelper.GetParent(obj);
}
// Will return null if not found
return obj as ItemContainer;
}
else return null;
}
Did you checked this sample?
After some research I found what DragEventArgs contains an OriginalSource property with Name matches the name of target list when Drop event invoked.
I'm not checked it with folders and subfolders, but maybe OriginalSource will contain folder where item dropped.
<TextBlock Grid.Row="1" Margin="8,4"
VerticalAlignment="Bottom"
Text="All Items"/>
<ListView x:Name="SourceListView"
Grid.Row="2" Margin="8,4"
SelectionMode="Extended"
CanDragItems="True"
DragItemsStarting="SourceListView_DragItemsStarting"/>
<TextBlock Grid.Row="1" Grid.Column="1" Margin="8,4"
VerticalAlignment="Bottom"
Text="Selection"/>
<ListView x:Name="TargetListView"
Grid.Row="2" Grid.Column="1" Margin="8,4"
AllowDrop="True" CanReorderItems="True" CanDragItems="True"
DragOver="TargetListView_DragOver"
Drop="TargetListView_Drop"
DragItemsStarting="TargetListView_DragItemsStarting"
DragItemsCompleted="TargetListView_DragItemsCompleted"/>
And here is printscreen with fired breakpoint:
EDIT:
To get an item inside of TargetList you can do a trick.
I think you use DataTemplate to display custom list items ("folders"). You can see a sample below. As you see I add Grid_DragOver trigger.
<Page.Resources>
<DataTemplate x:Key="ListViewDataTemplate">
<Grid Margin="20,5" DragOver="Grid_DragOver"
BorderBrush="White" BorderThickness="5" AllowDrop="True">
<TextBlock Margin="10" LineHeight="40" FontSize="32" FontWeight="Bold"/>
</Grid>
</DataTemplate>
</Page.Resources>
This way Grid_DragOver will be invoked when mouse pointer enter inside the Grid in DataTemplate.
And if you use binding List<YourFolderClass> as data source, you'll get folder in DataContext. For example I used this:
var SampleData = new ObservableCollection<string>
{
"My Research Paper",
"Electricity Bill",
"My To-do list",
"TV sales receipt",
"Water Bill",
"Grocery List",
"Superbowl schedule",
"World Cup E-ticket"
};
You can see all code in gist.

How can I initialize TabItem content on TabItem load?

Is there a way to initialize Tab content on Tab load? I'm having an issue, when Tab is created it doesnt initialize it components immediately. Only when I click on Tab the content will show up.
<TabControl
ItemsSource="{Binding Items}">
<TabControl.ItemTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding TabName}"><TextBlock.Background><SolidColorBrush /></TextBlock.Background></TextBlock>
<Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" Padding="0" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}" BorderBrush="#00000000">
<Image Source="/WPF_AccApp;component/Images/11.gif" Height="11" Width="11"></Image>
</Button>
<DockPanel.Background>
<SolidColorBrush />
</DockPanel.Background>
</DockPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<views:TabItemView />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Button click event
private void AddInvoice_Click(object sender, RoutedEventArgs e)
{
count++;
string s = string.Format("Tab {0}", count);
mainViewModel.Items.Add(new ItemViewModel(s));
this.DataContext = mainViewModel;
if (count == 1)
{
//this is as work around
// mainViewModel.Items.Add(new ItemViewModel(s));
// mainViewModel.Items.RemoveAt(1);
}
}
Found solution https://social.msdn.microsoft.com/Forums/vstudio/en-US/8043441a-95bb-4f70-b64c-a76f89980068/tabcontrol-getting-the-work-done-in-viewmodel-when-selectionchanged?forum=wpf
You need to add Selected Tab Index property and bind it to TabControl

Windows Phone 8.1 Listview unique first and last item template

Last year I made an app for Windows Phone 8, containing a LongListSelector with a unique first and last item template, as described here:
LongListSelector different item template for first and last item
I recently updated the app to Windows Phone 8.1 Store and this functionality broke. Here's my class (the only difference from the post being, that I'm using a ListView instead of a LongListSelector for Windows Phone 8.1 Store, since LongListSelector is not a part of the Windows Phone 8.1 Store framework):
public abstract class TemplateSelector : ContentControl
{
public abstract DataTemplate SelectTemplate(object item, int index, int totalCount, DependencyObject container);
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
var parent = GetParentByType<ListView>(this);
var index = (parent.ItemsSource as IList).IndexOf(newContent);
var totalCount = (parent.ItemsSource as IList).Count;
ContentTemplate = SelectTemplate(newContent, index, totalCount, this);
}
private static T GetParentByType<T>(DependencyObject element) where T : FrameworkElement
{
T result = null;
DependencyObject parent = VisualTreeHelper.GetParent(element);
while (parent != null)
{
result = parent as T;
if (result != null)
{
return result;
}
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
}
The problem is that this:
DependencyObject parent = VisualTreeHelper.GetParent(element);
of the GetParentByType function returns null for some reason. Anyone knows why or have an alternative solution?
Below is my XAML code(with a few xmlns's removed). The DataTrigger is there because sometimes a unique first item template is enough (controlled by the LoadMore property of the ViewModel, which is a bool).
<controls:WP81Page
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<controls:WP81Page.Resources>
<DataTemplate x:Key="first">
<Grid HorizontalAlignment="Left" Width="Auto" Height="200">
<Border BorderThickness="1" BorderBrush="Black" Visibility="{Binding NoLargeImage, Converter={StaticResource BoolToVisibilityConverter}}" >
<Image Source="/Images/default-image.png" Stretch="UniformToFill" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Visibility="{Binding NoLargeImage, Converter={StaticResource BoolToVisibilityConverterReverse}}" >
<Image Source="{Binding LargeImageUrl}" Stretch="UniformToFill" />
</Border>
<StackPanel VerticalAlignment="Bottom" Background="#7F000000" >
<TextBlock Text="{Binding Header}" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="White" FontWeight="Bold" Margin="15,0,15,15"/>
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="default">
<StackPanel Orientation="Horizontal" Margin="0,10,0,0" Height="105" Width="Auto">
<Border BorderThickness="1" BorderBrush="Black" Margin="0,2" Visibility="{Binding NoSmallImage, Converter={StaticResource BoolToVisibilityConverter}}" >
<Image Source="/Images/default-image.png" Width="130" Height="100" Stretch="UniformToFill" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Margin="0,2" Visibility="{Binding NoSmallImage, Converter={StaticResource BoolToVisibilityConverterReverse}}">
<Image Source="{Binding SmallImageUrl}" Width="130" Height="100" Stretch="UniformToFill"/>
</Border>
<StackPanel Orientation="Vertical" Width="300" Margin="8,0,0,0">
<TextBlock Text="{Binding Header}" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Margin="0,3,0,0" Text="{Binding DisplayDate}" TextWrapping="Wrap" Foreground="#FFB9B9B9" FontSize="16" />
</StackPanel>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="last">
<TextBlock Text="hent flere nyheder" FontSize="25" Margin="0,20" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" Height="75" />
</DataTemplate>
<DataTemplate x:Key="UniqueFirstTemplateSelector">
<common:UniqueFirstTemplateSelector Content="{Binding}" First="{StaticResource first}" Default="{StaticResource default}" HorizontalAlignment="Stretch"/>
</DataTemplate>
<DataTemplate x:Key="UniqueFirstAndLastTemplateSelector">
<common:UniqueFirstAndLastTemplateSelector Content="{Binding}" First="{StaticResource first}" Default="{StaticResource default}" Last="{StaticResource last}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</controls:WP81Page.Resources>
<controls:WP81Page.DataContext>
<viewModels:NewsViewModel/>
</controls:WP81Page.DataContext>
<Grid x:Name="LayoutRoot" Style="{Binding Source={StaticResource Background}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ProgressBar Grid.Row="0" VerticalAlignment="Top" IsIndeterminate="True" Background="Transparent" Foreground="White" Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibilityConverter}}" />
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="NYHEDER" FontSize="35" FontWeight="Bold" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1">
<controls:WP81ListView x:Name="listSelector" Margin="22,0" Grid.Row="2" ItemsSource="{Binding News}" Command="{Binding NewsEntrySelectedCommand}" >
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding LoadMore}" Value="True">
<core:ChangePropertyAction TargetObject="{Binding ElementName=listSelector}"
Value="{StaticResource UniqueFirstAndLastTemplateSelector}"
PropertyName="ItemTemplate" />
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding LoadMore}" Value="False">
<core:ChangePropertyAction TargetObject="{Binding ElementName=listSelector}"
Value="{StaticResource UniqueFirstTemplateSelector}"
PropertyName="ItemTemplate" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</controls:WP81ListView>
</Grid>
</Grid>
</controls:WP81Page>
Thank you in advance.
EDIT
Alright, so what I want is not for the first and last item to be unique all the time. I'm making a news feed where the last item is only unique when there are more news entries to load (when the last item are clicked, more news entries are added to the ListView). If however, the end of the news entries are reached, the last item must not be unique (hence the combination with interactivity).
ListView has a property called ItemTemplateSelector which accepts objects based on DataTemplateSelector. So you need to change a couple of things to get it to work.
First of all, the definition of your template selector. It needs to be based on DataTemplateSelector and override method called SelectTemplateCore. It takes an object which is a ListViewItem. At that point you can go up the VisualTree to get the ListView and choose the DataTemplate based on index.
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate First { get; set; }
public DataTemplate Default { get; set; }
public DataTemplate Last { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var listViewItem = container as ListViewItem;
var listView = GetParentByType<ListView>(listViewItem);
var index = (listView.ItemsSource as IList).IndexOf(item);
var totalCount = (listView.ItemsSource as IList).Count;
if (index == 0)
return First;
else if (index == totalCount - 1)
return Last;
else
return Default;
}
private T GetParentByType<T>(DependencyObject element) where T : FrameworkElement
{
T result = null;
DependencyObject parent = VisualTreeHelper.GetParent(element);
while (parent != null)
{
result = parent as T;
if (result != null)
{
return result;
}
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
}
Then, you need to create an instance of it in static resources
<local:MyTemplateSelector x:Key="SelectingTemplate"
First="{StaticResource first}"
Default="{StaticResource default}"
Last="{StaticResource last}" />
with the First, Default and Last DataTemplates, just like you did before.
The last step is to apply it to the ListView you're using.
<ListView ItemsSource="{Binding SomeItemsSource}"
ItemTemplateSelector="{StaticResource SelectingTemplate}" />
And that's it!

Windows phone 8 saving and open links in web Browser history

I have a web browser which is storing all the visited websites. There is just one issue, I would like it for the user to click on one of the records and then it should open in the webbrowser.
Once the user has navigated to a page, this method is called with the url:
public List<String> urls;
public string selectedURL;
public MainPage()
{
InitializeComponent();
listBox.DataContext = urls;
}
private void getHistory(string url)
{
urls.Add(url);
listBox.DataContext = null;
listBox.DataContext = urls;
}
private void listBoxtrend_Tap(object sender, GestureEventArgs e)
{
selectedURL = "";
var selected = listBox.SelectedValue as Item;
selectedText = selected.ItemString;
MessageBox.Show(selectedURL);
browserSearch(selectedURL);
}
This is then displayed into a textblock on a pivot page:
<phone:Pivot Margin="0,0,0,0">
<phone:PivotItem Header="" Margin="0,-104,0,0">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="72"/>
<RowDefinition Height="696"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#FF5E667B" >
</Grid>
</phone:PivotItem>
<phone:PivotItem Margin="0,-104,0,0" Header="">
<Grid>
<ListBox ItemsSource="{Binding Item}" Foreground="RoyalBlue" Name="listBox"
TabIndex="10" Tap="listBox_Tap" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" FontSize="26" HorizontalAlignment="Left"
x:Name="txtHistory" Text="{Binding ItemString}"
VerticalAlignment="Top" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</phone:PivotItem>
</phone:Pivot>
I have tried to put a click event, but there is one way to tell which record is being clicked. Is there a way to use the SelectionChanged event handler. And is there a better way to store this data, maybe in a array or list which then can be saved to IsolatedStorage.
Thank you in advance :)
If you need any more details please comment and I will be happy to explain in further detail :)
It's better if you would have the data you're going to display within a Listbox, I mean the Url's. So that you could easily get whatever the data you want from the clicked item. Make sure that you bind the source for your Listbox.
your xaml:
<ListBox ItemsSource="{Binding Item}" Foreground="RoyalBlue"
Height="395" HorizontalAlignment="Center"
Margin="12,111,0,0" Name="listBox"
VerticalAlignment="Top" Width="438"
TabIndex="10" Tap="listBox_Tap" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" FontSize="26" HorizontalAlignment="Left"
x:Name="txtHistory" Text="{Binding ItemString}"
VerticalAlignment="Top" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And then from your tap event handler of the Listbox
private async void listBoxtrend_Tap(object sender, GestureEventArgs e)
{
selectedText = "";
var selected = listBox.SelectedValue as Item;
selectedText = selected.ItemString;
MessageBox.Show(selectedText);
await Launcher.LaunchUriAsync(new Uri("give the url"));//here should be the selectedText
}
These can be referable for more:
Getting selected value of listbox windows phone 7
LIstbox Selected Item content to textblock
Hope it helps!

Make filter on wpf Listbox with Item template

I have a listbox in WPF databinded to a observablecollection
<ListBox Margin="0,0,-12,0" ItemsSource="{Binding ShopList}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Grid.Row="1"
Grid.ColumnSpan="2" KeyDown="ListBox_KeyDown" KeyUp="ListBox_KeyUp"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,17" >
<!--Replace rectangle with image-->
<Rectangle Height="50" Width="50" Stroke="Black" StrokeThickness="6" Margin="12,0,9,0"/>
<StackPanel Width="Auto">
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding Quantity}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I then have a filter method I want called
private void ShopItemDay_Filter(object sender, FilterEventArgs e)
{
var item = e.Item as ShopItem;
e.Accepted = item.Day == 1;
}
But I cant find any properties on the Listbox to use a filter method like done here http://www.galasoft.ch/mydotnet/articles/article-2007081301.aspx
You need to create a 'view' on your collection. See the documentation for CollectionView.Filter. The framework will create a default view for all bound collections. You can add a filter as follows:
ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers);
_customerView.Filter = CustomerFilter
private bool CustomerFilter(object item)
{
Customer customer = item as Customer;
return customer.Name.Contains( _filterString );
}
(From this tutorial);
As you can see in the article you linked to, the filter is not a property of the Control. It is a property of the CollectionViewSource which is a kind of wrapper around the collection. This wrapper allows for sorting, grouping and filtering.

Categories