ListBox items return string, when DataTemplate is Button
You need FrameworkTemplate.FindName Method (String, FrameworkElement)
for this purpose:
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
Then:
for (int i = 0; i < Game_ScrollViewer_online.Items.Count; i++)
{
ListBoxItem GameListBoxItem = (ListBoxItem)(Game_ScrollViewer_online.ItemContainerGenerator.ContainerFromIndex(i));
ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(GameListBoxItem);
DataTemplate myDataTemplate = contentPresenter.ContentTemplate;
Button tempBut = (Button) myDataTemplate.FindName("Current_game_button", contentPresenter);
//Do stuff with button
}
To solve missing FindName
use FindDescendant
like this:
public T FindDescendant<T>(DependencyObject obj) where T : DependencyObject
{
if (obj is T)
return obj as T;
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
if (childrenCount < 1)
return null;
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T)
return child as T;
}
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = FindDescendant<T>(VisualTreeHelper.GetChild(obj, i));
if (child != null && child is T)
return child as T;
}
return null;
}
listbox having buttons as data template does not display content from server
I see several problems with your Xaml:
- The listbox ItemSource binding (ItemsSource="{Binding Path=University}"), is meant to bind your listbox contents to the list of items you want to display. This needs to be something like ItemsSource="{Binding Path=Universities}", where Universities is an ObservableCollection of University objects.
- Universities must be a property of your data context, and should have a NotifyPropertyChanged method. The NotifyPropertyChanged method is what triggers the binding to occur.
Alternatively, you can remove the ItemSource binding and add the items directly to the list as you are doing in the DownloadedStringCompleted function - but you cannot do both (which is what your code is doing now).
However, you need to make a few more corrections:
- Notice that you are adding string objects to lstUniversities.Items, but your Button's DataTemplate is binding to the Name property of whatever object is in the list. So you either need to add the University objects to the list (not the strings) because University has a Name property or remove the Name binding.
Note that the DataContext for a listbox item is whatever object is in the lstUniversities.Items collection, so in this case a string.
To simplify things, since your University class has only one property, Name, why don't you do the following:
Keep your code in the DownloadStringCompleted function ==> here you are manually adding the items (in this case strings) instead of binding the listbox items source to an ObservableCollection of University objects (note: you might want to clear the Items list before you add items to it)
Change your XAML to the XAML below - I've removed the ItemsSource binding and the Name binding
2a. The ItemsSource binding interferes because you are manually populating the ItemsSource and it is also wrong because it binds to a University object not an ObservableCollection of University objects.
2b. The Name binding doesn't work because you are adding strings to the listbox's Items Source and a string does not have a Name property
2c. Any property you want to bind to must use the INotifyPropertyChanged interface. Since you are no longer binding to a property this is not necessary, but if do add bindings you should look this up in MSDN and learn how to use it.
<ListBox x:Name="lstUniversity" Height="532" FontSize="30" VerticalAlignment="Bottom" Margin="-1,0,1,110">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="1">
<Button Content="{Binding}"
Height="100" Width="550"
FontSize="24"
HorizontalAlignment="Stretch"
CommandParameter="{Binding}" Foreground="White"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Change selected ListBox Item DataTemplate on button click
I think the best option would be to add a property to your Person
model to tell the UI that you are editing and use that property change the Template
Here is a rough working example of what I mean, in this example when you set a Person
item to IsEditing
it will change the Template
to PersonEdit
otherwise it will set the Template
Person
Xaml:
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="399" Width="464" Name="UI" >
<Window.Resources>
<DataTemplate x:Key="Person">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
<DataTemplate x:Key="PersonEdit">
<TextBox Text="{Binding Name}"/>
</DataTemplate>
<Style TargetType="{x:Type ListBoxItem}" x:Key="PersonStyle">
<Setter Property="ContentTemplate" Value="{StaticResource Person}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditing}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource PersonEdit}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid DataContext="{Binding ElementName=UI}">
<ListBox Margin="5,5,5,30"
ItemsSource="{Binding Persons}"
SelectedItem="{Binding SelectedPerson}"
ItemContainerStyle="{StaticResource PersonStyle}" />
<Button Margin="5,333,369,5" Content="Add Person" Click="Button_Click" />
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
private Person _selectedPerson;
private ObservableCollection<Person> _persons = new ObservableCollection<Person>();
public MainWindow()
{
InitializeComponent();
Persons.Add(new Person { Name = "Stack" });
Persons.Add(new Person { Name = "Overflow" });
}
public ObservableCollection<Person> Persons
{
get { return _persons; }
set { _persons = value; }
}
public Person SelectedPerson
{
get { return _selectedPerson; }
set { _selectedPerson = value; }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SelectedPerson.IsEditing = true;
}
}
Person class:
public class Person : INotifyPropertyChanged
{
private string _name;
private bool _isEditing;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public bool IsEditing
{
get { return _isEditing; }
set { _isEditing = value; NotifyPropertyChanged("IsEditing"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Get DataTemplate from data object in ListBox
If you have scope to add a new dependency property to the EditableTextBlock
user control you could consider adding one that has the name StartupInEditMode
, defaulting to false
to keep the existing behavior.
The Loaded
handler for the UserControl
could then determine the status of StartupInEditMode
to decide how to initially set the value of IsInEditMode
.
//..... Added to EditableTextBlock user control
public bool StartupInEdit
{
get { return (bool)GetValue(StartupInEditProperty); }
set { SetValue(StartupInEditProperty, value); }
}
public static readonly DependencyProperty StartupInEditProperty =
DependencyProperty.Register("StartupInEdit", typeof(bool), typeof(EditableTextBlock ), new PropertyMetadata(false));
private void EditableTextBlock_OnLoaded(object sender, RoutedEventArgs e)
{
IsInEditMode = StartupInEditMode;
}
For controls already in the visual tree the changing value of StartupInEdit
does not matter as it is only evaluated once on creation. This means you can populate the collection of the ListBox
where each EditableTextBlock
is not in edit mode, then swap the StartupInEditmMode
mode to True
when you start adding new items. Then each new EditableTextBlock
control starts in the edit mode.
You could accomplish this switch in behavior by specifying a DataTemplate
where the Binding
of this new property points to a variable of the view and not the collection items.
<DataTemplate DataType="local:Column">
<utils:EditableTextBlock x:Name="editableTextBlock"
Text="{Binding Name, Mode=TwoWay}"
StartupInEditMode="{Binding ANewViewProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</DataTemplate>
You need to add a property to the parent Window
(or Page
or whatever is used as the containter for the view) called ANewViewProperty
in this example. This value could be part of your view model if you alter the binding to {Binding DataContext.ANewViewProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}
.
This new property (ANewViewProperty
) does not even need to implement INotifyPropertyChanged
as the binding will get the initial value as it is creating the new EditableTextBlock
control and if the value changes later it has no impact anyway.
You would set the value of ANewViewProperty
to False
as you load up the ListBox
ItemSource
initially. When you press the button to add a new item to the list set the value of ANewViewProperty
to True
meaning the control that will now be created starting up in edit mode.
Update: The C#-only, View-only alternative
The code-only, view-only alternative (similar to user2946329's answer)is to hook to the ListBox.ItemContainerGenerator.ItemsChanged
handler that will trigger when a new item is added. Once triggered and you are now acting on new items (via Boolean DetectingNewItems
) which finds the first descendant EditableTextBlock
control for the appropriate ListBoxItem
visual container for the item newly added. Once you have a reference for the control, alter the IsInEditMode
property.
//.... View/Window Class
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
MyListBox.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
}
private void ItemContainerGenerator_ItemsChanged(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
{
if ((e.Action == NotifyCollectionChangedAction.Add) && DetectingNewItems)
{
var listboxitem = LB.ItemContainerGenerator.ContainerFromIndex(e.Position.Index + 1) as ListBoxItem;
var editControl = FindFirstDescendantChildOf<EditableTextBlock>(listboxitem);
if (editcontrol != null) editcontrol.IsInEditMode = true;
}
}
public static T FindFirstDescendantChildOf<T>(DependencyObject dpObj) where T : DependencyObject
{
if (dpObj == null) return null;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dpObj); i++)
{
var child = VisualTreeHelper.GetChild(dpObj, i);
if (child is T) return (T)child;
var obj = FindFirstChildOf<T>(child);
if (obj != null) return obj;
}
return null;
}
Update #2 (based on comments)
Add a property to the view that refers back to the the ViewModel assuming you keep a reference to the View Model in the DataContext
:-
..... // Add this to the Window/Page
public bool DetectingNewItems
{
get
{
var vm = DataContext as MyViewModel;
if (vm != null)
return vm.MyPropertyOnVM;
return false;
}
}
.....
Selecting ListBox item returns null
SelectedItem
doesn't return a ListBoxItem
. It returns an instance of the type (Device
?) where the Imei
property is defined.
So you should cast to this type:
var myselectedItem= imeiListBox.SelectedItem as Device;
if (myselectedItem != null)
string text = myselectedItem.Imei.ToString();
Or you could use the dynamic
keyword:
dynamic myselectedItem= imeiListBox.SelectedItem;
string text = myselectedItem.Imei?.ToString();
Note that this will fail at runtime if SelectedItem
returns anything else than an object with an Imei
property. If you know the type, casting is preferable.
Controls Present within DataTemplate of ItemsControl on Button Click
Try this:
private void OnClick(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
Panel panel = btn.Parent as Panel;
TextBox tbx = panel.Children[0] as TextBox;
string s = tbx.Text;
ComboBox cmb = panel.Children[1] as ComboBox;
object item = cmb.SelectedItem;
}
But note that a better approach would be to bind the Text
property of the TextBox
and the SelectedItem
property of the ComboBox
to some properties of your Teacher
class and access these instead.
Get value of listbox item with item template
private void lbIps_bnClose_Click(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as [yourViewModelName];
var button = sender as Button;
var item = (string)button.DataContext;
vm.IpList.Remove(item);
}
How can I select the right ListBox Item automatically when I press the Button in the Item I want to Check to delete this Item?
In RemoveMark_Click
the first argument (typically called sender
and of type object
) should by the Button
you clicked on, and its DataContext should be your collection item to remove.
So change RemoveMark_Click to sth like:
void RemoveMark_Click(object sender, RoutedEventArg args)
{
//TODO add null checks a/o exception handling
var uiElement = (Button)sender;
var dataItem = uiElement.DataContext;
tbxFiles.Items.Remove(dataItem);
}
Generally spoken, for a button, it is more common and good practice to use a Command to execute the desired action, because then you can put the magic in the ViewModel.
Also, in order to have a special layout for a listboxitem setting the datatemplate a/o the itemcontainerstyle of the listbox will typically suffice, you don't need to override the control template, but of course you can and it will work.
Related Topics
Capture the Screen Shot Using .Net
Retrieve System Uptime Using C#
How to Return Anonymous Type from C# Method That Uses Linq to SQL
A Way of Casting a Base Type to a Derived Type
Why Can't Yield Return Appear Inside a Try Block with a Catch
Why We Need Thread.Memorybarrier()
Ef 4.1 - Code First - JSON Circular Reference Serialization Error
Error Accessing Com Components
Leave Only Two Decimal Places After the Dot
Why Are C# 4 Optional Parameters Defined on Interface Not Enforced on Implementing Class
How to Instantiate a Dbcontext in Ef Core
Console.Writeline and Generic List
How to Find Default Web Browser Using C#
Passing List<> to SQL Stored Procedure