Listbox Items Return String, When Datatemplate Is Button

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:

  1. 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.
  2. 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:

  1. 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:

  1. 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)

  2. 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



Leave a reply



Submit