I want To access named controls inside listview datatemplet i followed this: How to Access a Named Control Inside a XAML DataTemplate (using CSharp
itemlistview.ItemsSource=new List<MyObject>();
foreach (var item in itemListView.Items)
{
var _Container = itemListView.ItemContainerGenerator
.ContainerFromItem(item);
var _Children = AllChildren(_Container);
var _FirstName = _Children.OfType<StackPanel>()
.First(x => x.Name.Equals("subjectListItem"));
_FirstName.Visibility =
Visibility.Collapsed;
}
But the problem in var item in itemListView.Items it retruns the MyObject which i passed to listview ItemsSource Not the controles inside the datatemplete.
So How can I return The Controls ?
ItemContainerGenerator.ContainerFromItem(object) should return an instance of ListViewItem that contains the data of the item.
But, this practice is discourage in WPF/WinRT frameworks. The "right" way is to control the data and have the UI react to it.
Something like:
// in code
var list = new List<MyObject>();
foreach (var item in list)
{
item.IsSomethingVisible = false;
}
itemlistview.ItemsSource = list;
<!--in XAML-->
<StackPanel Visibility="{Binding IsSomethingVisible,
Converter={StaticResource BooleanToVisibilityConverter}}".../>
<!--The converter is defined in resources of some higher level-->
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
Related
I'm trying to make a NavigationViewMenu and I need a menu layed out as follows
static Home item
static Header
dynamic elements from DB as items
static Header
static set of items
This is what I tried:
<NavigationView.MenuItems>
<NavigationViewItem Icon="Home" Content="Home" Tag="home" />
<NavigationViewItemSeparator />
<NavigationViewItemHeader Content="My Stuff"/>
<NavigationViewList ItemsSource="{x:Bind MyStuff}">
<NavigationViewList.ItemTemplate>
<DataTemplate x:DataType="local:MyModel">
<NavigationViewItem Icon="Pictures" Content="{x:Bind Name}" Tag="{x:Bind Tag}" />
</DataTemplate>
</NavigationViewList.ItemTemplate>
</NavigationViewList>
<!-- Static equivalent to the above:
<NavigationViewItem Icon="Pictures" Content="Woop" Tag="foos"/>
<NavigationViewItem Icon="Pictures" Content="Doop" Tag="foos"/>
<NavigationViewItem Icon="Pictures" Content="Loop" Tag="foos"/>
-->
<NavigationViewItemHeader Content="Other Stuff"/>
<NavigationViewItem Icon="Pictures" Content="Foos" Tag="foos"/>
<NavigationViewItem Icon="ContactInfo" Content="Bars" Tag="bars"/>
<NavigationViewItem Icon="SwitchApps" Content="Bazes" Tag="bazes"/>
</NavigationView.MenuItems>
This is what I've got:
This is what I wanted:
Is there anything as good and practical as Angular's *ngFor in XAML for UWP?
I ran into the same behavior, and managed to find a work around. In my case, I had two lists of menu items (dynamically data-bound items), and I wanted to use NavigationViewItemHeader on top of both (static items). I tried using a NavigationViewList and ran into your problem.
TL;DR:
Create a list of menu items in C# code. The elements of this list can be a mix of your viewmodels, and any static Navigation Items (headers, separators, etc). Then use a DataTemplateSelector to either databind to your viewmodel or pass-through the navigation items unchanged.
More detailed
In your C# code-behind, create an enumerable (or observable collection) of your menu items. In my case SomeCollection and AnotherCollection represent my data sources that I wanted to bind to my NavigationView. I have to type it as object because it's a mix of my viewmodels and the built-in UWP navigation item types.
private IEnumerable<object> MenuItems()
{
yield return new NavigationViewItemHeader { Content = "Some List" };
foreach (var some in SomeCollection)
{
yield return some;
}
yield return new NavigationViewItemHeader { Content = "Another List" };
foreach (var another in AnotherCollection)
{
yield return another;
}
}
// somewhere else, like in your Page constructor or a CollectionChanged handler
this.NavigationList = MenuItems().ToList();
Second, create a Data Template Selector to switch between your template and the navigation items:
class NavigationItemTemplateSelector : DataTemplateSelector
{
public DataTemplate ViewModelTemplate{ get; set; }
public DataTemplate NavigationItemTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
return item is MyViewModel
? ViewModelTemplate
: NavigationItemTemplate;
}
}
Finally, change your NavigationView to reference the template selector and menu item source. The NavigationItemTemplate is just a pass-through, and your ViewModelTemplate would have the normal viewmodel item binding logic.
<Page.Resources>
<DataTemplate x:Key="ViewModelTemplate" x:DataType="local:MyViewModel">
<TextBlock Text="{x:Bind SomeProperty}" />
</DataTemplate>
<DataTemplate x:Key="NavigationItemTemplate">
</DataTemplate>
<local:NavigationItemTemplateSelector x:Key="NavigationItemTemplateSelector"
ViewModelTemplate="{StaticResource ViewModelTemplate}"
NavigationItemTemplate="{StaticResource NavigationItemTemplate}" />
</Page.Resources>
<NavigationView
MenuItemsSource="{x:Bind NavigationList, Mode=OneWay}"
MenuItemTemplateSelector="{StaticResource NavigationItemTemplateSelector}">
<Frame x:Name="ContentFrame"></Frame>
</NavigationView>
I can reproduce it. It looks like NavigationViewList only take the space of one item when putting itself in NavigationView.MenuItem. Which is the same like putting a ListView in a ListViewItem. To change this behavior we need to change the item's behaviour ourselves. However after some investigating it seems currently customization of NavigationViewList is blackbox for us. So the only way I could think is to build our own NavigationView with the help of splitview and acrylic.
I didn't find it necessary to use different templates as in the accepted answer, maybe because there were some changes in the underlying Windows code in the meantime. As I needed a stable part of the menu and then a dynamic part depending on the actual page, I created an interface:
interface IMenuProvider {
IEnumerable<NavigationViewItemBase> GetMenuItems();
}
and made sure all my pages implement it. My MainPage returns the fixed part:
public IEnumerable<NavigationViewItemBase> GetMenuItems() {
yield return new NavigationViewItem {
Tag = "home",
Icon = new SymbolIcon(Symbol.Home),
Content = "Home",
};
yield return new NavigationViewItemSeparator();
yield return new NavigationViewItem {
Tag = "xxx",
Icon = new SymbolIcon(Symbol.XXX),
Content = "XXX",
};
}
the other pages, similary, provide their own menu headers and items.
When I navigate the pages, I change the menu as well, concatenating the fixed and variable parts:
ContentFrame.Navigate(PageType, null, transitionInfo);
if (ContentFrame.Content is IMenuProvider menuProvider)
= GetMenuItems().Concat(menuProvider.GetMenuItems()).ToList();
(Or, you might place the menu change into the Navigated handler of the Frame.)
While it's still a nuisance that these menus, at least the fixed part, cannot be declared in XAML, this approach works.
I'm extending a control to be able to reuse it across my current Xamarin project. As part of this control, I need to create a DataTemplate programmatically. I have this part figured out and it works ok.
The DataTemplate has a Label in it. I need to bind the Label's BindingContext property to {Binding Source}. I need to bind the Label's Text property to {Binding Path=Name}.
This works in XAML, but I don't want to have to copy it to a million different places in the code base.
<dxGrid:TemplateColumn FieldName="MyPropertyName"
Caption="MyColumn">
<dxGrid:TemplateColumn.DisplayTemplate>
<DataTemplate>
<Label BindingContext="{Binding Source}"
Text="{Binding Source, Path=MyPropertyName}" />
</DataTemplate>
</dxGrid:TemplateColumn.DisplayTemplate>
My extended control looks like this right now:
public class MyColumn : TemplateColumn
{
public MyColumn()
{
DataTemplate displayTemplate = new DataTemplate(() =>
{
BindingBase textBinding = new Binding(FieldName);
Label label = new Label();
// TODO: Bind BindingContextProperty to {Binding Source}
//label.SetBinding(BindingContextProperty, binding);
label.SetBinding(Label.TextProperty, textBinding);
return new ViewCell
{
View = label
};
});
DisplayTemplate = displayTemplate;
}
}
I'm getting hung up in the binding because I'm not sure how to do the equivalent of {Binding Source} in code. Any help would be appreciated.
#Eugene - Thanks for the response. Unfortunately this does not work and binding to "Source" like that throws a Null Reference Exception. I made another pass at it this morning and got it working this way:
public MyColumn()
{
DataTemplate displayTemplate = new DataTemplate(() =>
{
Grid grid = new Grid();
grid.SetBinding(Grid.BindingContextProperty, "Source");
Label label = new Label();
label.SetBinding(Label.TextProperty,FieldName);
grid.Children.Add(label);
return grid;
});
this.DisplayTemplate = displayTemplate;
}
It's simple, use name of property
label.SetBinding(BindingContextProperty, "Source");
I realised that storyboard only working with silverlight Wp 8.1 apps. So what about non-silverlight? I've want to make an animation of smooth appearing elements of listview after page transition. How can i make it? I check VisualStateGroup, but to make animation like that i should make a code behind. Is it possible to make only by XAML?
If you want to use XAML you can use the built in transitions which you can choose from already predefined ones. One drawback is that you can't create your own transitions this way.
To change the item transitions change the ItemContainerTransitions property on your ListView:
<ListView x:Name="MyListView">
<ListView.ItemContainerTransitions>
<TransitionCollection>
<!--<ContentThemeTransition HorizontalOffset="0"/>-->
<!--<EntranceThemeTransition />-->
<!-- <AddDeleteThemeTransition /> -->
<!-- <RepositionThemeTransition/> -->
<!-- <ReorderThemeTransition /> -->
<!-- <PopupThemeTransition/> -->
<!-- <EdgeUIThemeTransition Edge="Top"/> -->
</TransitionCollection>
</ListView.ItemContainerTransitions>
</ListView>
You can combine them if you want. Just uncomment any of the above code and use which suits you best.
You can create custom item animations when you extend ListView and override the GetContainerForItemOverride or PrepareContainerForItemOverride methods. You can use XAML if you want for example a Storyboard defined in as a StaticResource. Here is an example:
public class MyListView : ListView
{
protected override DependencyObject GetContainerForItemOverride()
{
var container = base.GetContainerForItemOverride();
var da = new DoubleAnimation();
// init da...
var sb = new Storyboard();
// initi sb with container
sb.Begin();
return container;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var da = new DoubleAnimation();
// init da...
var sb = new Storyboard();
// initi sb with element
sb.Begin();
}
}
I'm working with LINQ/SQL within my WP8 app to manage a collection of items that are shown within a ListBox.
Sometimes one these objects is changed by a long runned operation using another DataContext.
These changes could be one of the CRUD operations, so objects can be created in the background, too.
In iOS there is the NSFetchedResultsController for it or i can use a NSNotification - i'm searching for something similar.
update some code for an updating case
I'm fetching my items like this:
void loadData() {
var items = from r in itemDB.items orderby r.UpdatedAt descending select r;
var Items = new ObservableCollection<Item>(items);
lstData.ItemsSource = Items;
}
my listBox looks like this
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="10" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding LocalState}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and my background code looks like this:
static public void UploadAll() {
ThreadPool.SetMaxThreads(1,1);
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
}
static void ThreadProc(Object stateInfo)
{
ItemsContext itemsDB = new ItemsContext(ItemsContext.DBConnectionString);
var notUploadedItems = (from i in itemsDB.items
where !i.LocalState.Equals("server")
select i);
var Items = new ObservableCollection<Item>(notUploadedItems);
foreach (Item a in Items)
{
a.Save(() =>
{
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
a.LocalState = "server";
itemsDB.SubmitChanges();
});
//NOW THE LISTBOX SHOULD BE UPDATED
}, (err) =>
{
});
}
}
at the //NOW THE LISTBOX SHOULD BE UPDATEDcomment, the corresponding listbox template should be updated
add/delete cases are similar.
cheers.
To be straight to the point, creating new instance of the ObservableCollection every time your data changes then re-assigning it to the ItemSource, is not only disregarding core purpose of the ObservableCollection, it is also breaking/misusing the concept of Data
Binding.
//this is the line I am talking about:
var Items = new ObservableCollection<Item>(notUploadedItems);
Instead, create the ObserbaleCollection once -- make it a property:
private readonly ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items {get {return _items;}
For the updates use collection's '.Add()' or 'Remove()' methods
You're correct that you must be on the UI Main thread to do the updates:
DoItOnUIThread(() =>Items.Add(newItem));
//here's a sample of method that would take any action and execute it on UI thread..
private void DoItOnUIThread(Action action)
{
if (_dispatchService == null)
_dispatchService = ServiceLocator.Current.GetInstance<IDispatchService>();
if (_dispatchService.CheckAccess())
action.Invoke ();
else
_dispatchService.Invoke(action);
}
Let the observable collection do its job, which is automatically update the UI through Binding with new items and their property changes. If all that is hooked up/done properly your listbox shouldn't need to be updated in cs code at all.
I have a ColumnSeries chart where I want to control the selected item from the view model. I do this by binding the the SelectedItem of the chart to an object on the view model.
<chartingToolkit:Chart Grid.Row="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" BorderThickness="0" MinHeight="200" Margin="0" x:Name="ratingsChart" Style="{StaticResource ChartWithoutLegendStyle}">
<chartingToolkit:Chart.Series>
<chartingToolkit:ColumnSeries x:Name="chartRatingColSeries" IsSelectionEnabled="True"
SelectedItem="{Binding SelectedRatingDistribution, Mode=TwoWay}"
ItemsSource="{Binding RatingsList}"
IndependentValueBinding="{Binding RatingName}"
DependentValueBinding="{Binding NumberOfGoodies}">
</chartingToolkit:ColumnSeries>
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>
There are various elements on the page which will force the chart's data to be reloaded (via a web service). When I need to reload the charts data (from the view model), I want to set the SelectedItem of the chart to the very first data point. This appears to work EXCEPT the chart does not visually show (in red, by default) the selected item. Here is sample code that reloads data after web service call and resets selected item:
private RatingDistribution _selectedRatingDistribution = new RatingDistribution();
public RatingDistribution SelectedRatingDistribution
{
get { return _selectedRatingDistribution; }
set
{
_selectedRatingDistribution = value;
RaisePropertyChanged("SelectedRatingDistribution");
}
}
private ObservableCollection<RatingDistribution> _lstRatings = new ObservableCollection<RatingDistribution>();
public ObservableCollection<RatingDistribution> RatingsList
{
get { return _lstRatings; }
set
{
_lstRatings = value;
RaisePropertyChanged("RatingsList");
}
}
private void GetRatingsDistributionCompleted(object sender, GetRatingsDistributionCompletedEventArgs e)
{
IsBusy = false;
RatingsList.Clear();
foreach (RatingDistribution rd in e.Result)
RatingsList.Add(rd);
SelectedRatingDistribution = RatingsList[0];
}
Setting the SelectedRatingDistribution from the View model will not visually show the selected item on the chart in red. Any ideas??
Update:
So if I click on a column, the chart correctly shows the selected item in red as so:
But If I set the SelectedItem from view model, the column will not be displayed in red (as the selected item)
Changed function below fixes problem..
private void GetRatingsDistributionCompleted(object sender, GetRatingsDistributionCompletedEventArgs e) {
RatingsList.Clear();
foreach (RatingDistribution rd in e.Result)
RatingsList.Add(rd);
App.Current.RootVisual.Dispatcher.BeginInvoke(new Action(delegate() { SelectedRatingDistribution = RatingsList[0]; }));
}