For some reason I have to initialize the ListBox items in behind code, the reason is too complication to tell.
The LoadFamily() is called during WPF UserControl show up.
public void LoadFamily()
{
DataTemplate listItemTemplate = this.FindResource("ManDataTemplate") as DataTemplate;
foreach (Person man in family)
{
ListBoxItem item = new ListBoxItem();
item.DataContext = man;
item.ContentTemplate = listItemTemplate;
// other initialize for item object
this.ActivityList.Items.Add(item);
}
}
In my xmal file, I define a DataTemplate.
<DataTemplate x:Key="ManDataTemplate" DataType="{x:Type local:Person}">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
But the ListBox only contains empty text block, the person's Name doesn't not display. I don't know why, maybe the data binding is wrong, but how to do it in behind code.
Thanks for your any help! (WPF 3.5 or 4.0)
/////////////////////////////////////////////////////////////
Thanks for all your help. I found where I was wrong.
I should not add ListBoxItem into ActivityList.Items, one is UIElement, the other is >DataCollection. They are two different thing.
I should modify the code as follow:
foreach (Person man in family)
{
this.ActivityList.Items.Add(man);
ListBoxItem item = this.ActivityList.ItemContainerGenerator.ContainerFromItem(man) as ListBoxItem;
item.ContentTemplate = listItemTemplate;
// other initialize for item object
}
I don't see the benefit of creating the listboxitems manually. Just set the ItemsSource of the Listbox to the list of person (family).
There could be binding error. That's the reason why the textblock is empty. Check the Output window of VisualStudio, it will display the binding errors if exist.
HTH
Related
I want to bind TabItems from list to TabControl:
<TabControl Name="TabsControl" ItemsSource="{Binding}"/>
TabObject is generated in code behind:
private List<TabObject> tabsList = new List<TabObject>();
TabObject MyTab = new TabObject(Tabitem tabitem, object genericObject);
tabsList.Add(MyTab);
TabsControl.DataContext = tabList
How to bind only TabItems object from tabsList to TabsControl, is it possible without creating a separate list for TabItems?
You can use DisplayMemberPath and SelectedValuePath to use tabItem as name and as selectedValue.
Your TabControl would end up like this:
<TabControl Name="TabsControl" ItemsSource="{Binding}" DisplayMemberPath="Tabitem" SelectedValuePath="Tabitem"/>
I wrote Tabitem because i don't know that the name of the property of type Tabitem of the object of type TabObject is called. if you access the item as tabObject.Item obviously you would have to write there only Item.
I hope I was helpful.
I have a ListView that contains several types of custom UserControls.
The project requires that some of them must be non-clickable, so I would like to disable them, but JUST THEM.
Those items will be enabled/disabled depending on the value of a custom property.
I've tried to set the ListViewItem.IsEnabled property to false, but it ain't worked, and the other solutions I've found around make no sense to me...
I let a sample of the code:
XAML
<ListView x:Name="homeLW"
Margin="0,5,0,0"
ItemClick="homeLW_ItemClick"
IsItemClickEnabled="True"
HorizontalAlignment="Center"
ItemsSource="{Binding Source}">
Where Source is a ObservableCollection<UserControl>.
The problem is that I can't get the items of the ListView as ListViewItems, but as the UserControl type:. When executing this:
foreach(ListViewItem lwI in homeLW.Items)
{
//CODE
}
I get:
System.InvalidCastException: Unable to cast object of type
UserControl.Type to type Windows.UI.Xaml.Controls.ListViewItem.
Anyone know how could I make it?
Thanks in advance :)
foreach(var lwI in homeLW.Items)
{
ListViewItem item =(ListViewItem)homeLW.ContainerFromItem(lwI);
item.IsEnabled = false;
}
When on load all ListViewItems wont be loaded because of Virtualization. So you get Null when try to get container from item. Workaround would be switching off the virtualization. But it will have performance effects. Since you confirmed that it wont be having more than 20 items,I ll go ahead and add the code
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
To add onto LoveToCode's answer, if you want to disable the selected items on load and not turn off Virtualization, you'll need to fire the code when the UIElement is loaded. Otherwise, you'll get a System.NullReferenceException. The reason for this is because the Framework Element hasn't been loaded to reference the ListView Container.
homeLW.Loaded += DisableSelectedItemsOnLoad()
private void DisableSelectedItemsOnLoad()
{
foreach(var lwI in homeLW.Items)
{
ListViewItem item =(ListViewItem)homeLW.ContainerFromItem(lwI);
item.IsEnabled = false;
}
}
We have a requirement to display the images in a GridView incrementally. So to find the selected items in GridView , IsSelected property of the GridView item has bind with corresponding binding objects property of CLR object (property of the GridView's ItemSource type). Since, UWP does not support RelativeSouce and setter binding in style, so after doing some search on internet we found the below code.
public class GridViewEx : GridView
{
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var gridItem = element as GridViewItem;
var binding = new Binding { Mode = BindingMode.TwoWay, Source = item, Path = new PropertyPath("IsSelected") };
gridItem.SetBinding(SelectorItem.IsSelectedProperty, binding);
}
}
But it seems that there is a flaw with the above approach. Whenever the page is being scrolled down to load next set of photos then the previous selected items are losing their selection.
Is anyone has experienced this issue before or any suggestions to solve the above problem?
Move IsSelected to the model class and bind it to a user control. The user control actually is the cell and will be put in your data template.
<GridView.ItemTemplate>
<DataTemplate>
<controls:MyCustomControl IsSelected="{Binding IsSelected}"/>
</DataTemplate>
</GridView.ItemTemplate>
In MyCustomControl, you will handle difference visual state to show the selected status of item. This way your ViewModel dont have to "know" the list at all, when you need just get list of selected item from your list of models.
I have a Windows 8.1 application with a ListView and I am using ListViewExtensions from WinRt Xaml Toolkit(Obtained latest from Nuget) to bind BindableSelection
Here is my XAML
<ListView
ItemsSource="{Binding AllItems}"
SelectionMode="Multiple"
ext:ListViewExtensions.BindableSelection="{Binding SelectedItems, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In my ViewModel I have the following ObservableCollection which I have bound my xaml to
private ObservableCollection<string> _SelectedItems;
public ObservableCollection<string> SelectedItems
{
get { return _SelectedItems; }
set
{
if (value != _SelectedItems)
{
_SelectedItems = value;
NotifyPropertyChanged("SelectedItems");
}
}
}
I have put breakpoints on the get and set of my ObservableCollection. The get will be called as soon as my View loads, but the set is never called even though I select multiple items of my ListView.
Am I doing something wrong.
I would be very glad if someone can point me in the right direction.
Thanks in Advance.
Realized my mistake. I have never created the object for ObservableCollections SelectedItems.
One should create the object for the ObservableCollection at some point otherwise the XAML will be binding to a null object reference which obviously cannot be updated.
Here is how you instantiate the ObservableCollection.
SelectedItems = new ObservableCollection<MyItems>();
However I am still unable to hit the breakpoint of the set function of the ObservableCollection. I believe that this is the default behavior of the Observable. Would be glad if someone can comment on that.
Nevertheless the problem for this particular question is solved.
Thanks
Hi this should be faily simple, however I don't know what I am doing wrong. I've been looking all over the internet seeing people make this work, even followed the tutorial on MSDN still nothing has worked for me.
I want to Iterate over a ListBox, and get the ListBoxItems so I can find the DataTemplate that I have added to it.
This is my code behind.
private void SetListBoxDataTemplate(ListBox MyListBox)
{
try
{
foreach (CustomDataTemplateObject dataobject in MyListBox.Items)
{
ListBoxItem lbi = (ListBoxItem)(MyListBox.ItemContainerGenerator.ContainerFromItem(dataobject));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(lbi);
DataTemplate dt = myContentPresenter.ContentTemplate;
TextBlock tb = (TextBlock)dt.FindName("ListBoxItemTextBlock1", myContentPresenter);
ComboBox cb = (ComboBox)dt.FindName("ListBoxItemComboBox1", myContentPresenter);
tb.Text = dataobject.Text;
cb.ItemsSource = dataobject.ListColors;
}
}
catch (Exception ex)
{
MessageBox.Show(""+ex);
}
}
XAML looks like this:
<DataTemplate x:Key="ListBoxItemDataTemplate1">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="1 1 0 1" MinWidth="50">
<TextBlock Name="ListBoxItemTextBlock1" Background="{Binding ElementName=ListBoxItemComboBox1, Path=SelectedValue}" >
</TextBlock>
</Border>
<ComboBox Name="ListBoxItemComboBox1" />
</StackPanel>
</DataTemplate>*
<StackPanel>
<ListBox Name="ListBoxTest1" ItemTemplate="{DynamicResource ListBoxItemDataTemplate1}" />
</StackPanel>
I have tried with setting my itemtemplate to static to see if it works, and the method i'm calling from code behind, is called after I have populated my ListBoxs
My dataobject is NOT null, however when i call the line in my code behind, my lbi, ends up being null.
Any suggestions? thanks in advance!
FIRST UPDATE
This problem only occurs if i call the method in my constructor, so perhaps it's because it hasn't initialized the full group element section yet. However I want to do this as soon as possible. Am I perhaps forced to do it in a WindowLoaded event?
SECOND UPDATE
Code updated, Rachel's answer worked for iterating over my ListBoxItems, however the Listbox Has not fully rendered since i'm unable to reach the Datatemplate at this time. So MyListBox_GeneratorStatusChanged is not working for this problem, but it does get the ListBoxItems.
WPF's main thread runs items at different priority levels. Code that runs in the Constructor all gets run at Normal priority, while things like rendering the ListBox and it's items run at the Render priority level, which occurs after all Normal priority operations have finished.
This means that your entire Constructor gets run (including SetListBoxDataTemplate()) before your ListBox is even rendered and the items get generated.
If you want to run some code after the items are generated, use the ItemsContainerGenerator.StatusChanged event
// Constructor
MyListBox.ItemContainerGenerator.StatusChanged += MyListBox_GeneratorStatusChanged;
...
void MyListBox_GeneratorStatusChanged(object sender, EventArgs e)
{
// return if containers have not been generated yet
if (MyListBox.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return;
// remove event
MyListBox.ItemContainerGenerator.StatusChanged -= MyListBox_GeneratorStatusChanged;
// your items are now generated
SetListBoxDataTemplate(MyListBox);
}
What are you trying to accomplish with this method anyways? It is a bit unusual for WPF, and there may be a much better WPF way of accomplishing your task.
Updated based on new code added to Question
A much better method of setting your Text and ItemsSource properties is to make use of WPF's data bindings.
Your DataTemplate should look like this:
<DataTemplate x:Key="ListBoxItemDataTemplate1">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="1 1 0 1" MinWidth="50">
<TextBlock Text="{Binding Text}" Background="{Binding ElementName=ListBoxItemComboBox1, Path=SelectedValue}" >
</TextBlock>
</Border>
<ComboBox ItemsSource="{Binding ListColors}" />
</StackPanel>
</DataTemplate>*
A DataTemplate is like a cookie cutter. It's used to make the UI objects, but is not part of the UI object itself. All it does is tell WPF that "When you go to render this object, render it using this XAML". So the way your XAML gets rendered is
<ListBoxItem>
<StackPanel>
<Border>
<TextBlock Text="{Binding Text}" />
</Border>
<ComboBox ItemsSource="{Binding ListColors}">
</StackPanel>
</ListBoxItem>
In addition, the DataContext behind your ListBoxItem is the item from the collection bound to ListBox.ItemsSource, which based on your code should be CustomDataTemplateObject. That allows the bindings from the DataTemplate to work
If you're new to WPF and struggling to understand how exact the DataContext works, I'd recommend reading this article of mine: What is this "DataContext" you speak of?.
To summarize, WPF has two layers to an application: the UI layer and the Data Layer (DataContext). When you perform a basic binding like above, you are pulling data from the data layer into the UI layer.
So your ListBoxItem has a data layer of CustomDataTemplateObject, and the TextBlock.Text and ComboBox.ItemsSource bindings are pulling data from the data layer for use in the UI layer.
I'd also highly recommend using a utility like Snoop which lets you view the entire Visual Tree of a running WPF application to see how items get rendered. Its very useful for debugging or learning more about how WPF works.
You're confusing two jobs and mixing them into one. First, get access to the ListBoxItem:
private void SetListBoxDataTemplate(ListBox MyListBox)
{
foreach (ListBoxItem listBoxItem in MyListBox.Items)
{
}
}
Now you can get the DataTemplate from the ListBoxItem:
foreach (ListBoxItem listBoxItem in MyListBox.Items)
{
ContentPresenter presenter = FindVisualChild<ContentPresenter>(listBoxItem);
DataTemplate dataTemplate = presenter.ContentTemplate;
if (dataTemplate != null)
{
// Do something with dataTemplate here
}
}
The FindVisualChild method can be found in the How to: Find DataTemplate-Generated Elements page on MSDN.
UPDATE >>>
To answer your edit, yes, the constructor will be too early to try to access these DataTemplates because the Framework won't have applied them to all of the objects by then. It is best to use the FrameworkElement.Loaded Event to do these kinds of things, as that is the first event that can be called after the controls have all been initialised.