Forcing a tri-state checkbox to not move to indeterminate state - wpf

I'm working in WPF and i have an interesting requirement.
I need my checkboxes to be ThreeState, so if only some of the child elements are selected it shows as indeterminate. But when a use clicks it, i want it to select either true or false.
Here is a story to represent my requirements:
item - indeterminate
subItem - checked
subItem - unchecked
subItem - checked
When a user clicks item, the checkbox should alternate between checked and unchecked. The user should never be able to select 'indeterminate' as a state. Is this possible?

XAML:
<CheckBox IsThreeState="True" IsChecked="{x:Null}" Click="CheckBox_Clicked" />
Code-behind:
private void CheckBox_Clicked(object sender, RoutedEventArgs e)
{
var cb = e.Source as CheckBox;
if (!cb.IsChecked.HasValue)
cb.IsChecked = false;
}
If you don't like the code-behind solution, then you could sub-class your own control like in the solution for this question.

It's much easier if you use Binding with your CheckBox.
XAML:
<Window x:Class="WpfApplication39Checkbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<CheckBox Content="CheckBox" HorizontalAlignment="Left" Margin="128,95,0,0" VerticalAlignment="Top" IsThreeState="False" IsChecked="{Binding CheckState}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="46,241,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="139,241,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="235,241,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_2"/>
</Grid>
</Window>
Code-behind:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApplication39Checkbox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private bool? checkState;
public bool? CheckState
{
get { return checkState; }
set
{
checkState = value;
OnPropertyChanged("CheckState");
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
CheckState = false;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
CheckState = true;
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
CheckState = null;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You see the point? You set the CheckBox to IsThreeState="False" but set the third state from CodeBehind and the CheckBox behaves as expected.

Related

Toggle the checkbox status when double click in WPF

Created a simple checkbox in WPF.
The single click toggles the checkbox status in the UI.
I want the checkbox status to be changed when I double click on checkbox text. Please help me with code below.
The only control in the xaml:
<Grid>
<CheckBox Content="CheckBox" HorizontalAlignment="Left" Height="60" Margin="144,93,0,0" VerticalAlignment="Top" Width="392"/>
</Grid>
I could not find a solution with standart CheckBox Control. I've created my own checkBox via UserControl.
DoubleClickCheckBox.xaml (UserControl)
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Loaded="Grid_Loaded">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<CheckBox Name="checkBox" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked"/>
<TextBlock Name="textBlock" MouseDown="TextBlock_MouseDown" TextAlignment="Center" Grid.Column="1" Margin="4,0,4,0"/>
</Grid>
DoubleClickCheckBox.xaml.cs (UserControl)
public partial class DoubleClickCheckBox : UserControl
{
public string Text { get; set; }
public bool IsChecked { get; set; }
public event EventHandler UserControlChecked;
public event EventHandler UserControlUnChecked;
public DoubleClickCheckBox()
{
InitializeComponent();
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
this.checkBox.IsChecked = this.IsChecked;
this.textBlock.Text = this.Text;
}
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
if(e.ClickCount == 2)
this.checkBox.IsChecked = !this.checkBox.IsChecked;
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (UserControlChecked != null)
UserControlChecked(this, EventArgs.Empty);
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
if (UserControlUnChecked != null)
UserControlUnChecked(this, EventArgs.Empty);
}
}
MainWindow.xaml
<Grid>
<local:DoubleClickCheckBox VerticalAlignment="Center" HorizontalAlignment="Center" Background="Aqua" Text="Test" IsChecked="False" UserControlChecked="DoubleClickCheckBox_UserControlChecked" UserControlUnChecked="DoubleClickCheckBox_UserControlUnChecked"/>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void DoubleClickCheckBox_UserControlChecked(object sender, EventArgs e)
{
this.Title = "Checked";
}
private void DoubleClickCheckBox_UserControlUnChecked(object sender, EventArgs e)
{
this.Title = "UnChecked";
}
}
You can customize everything with this style. I customized only checked and unchecked events. It is up to you in future.
EDIT: As post owner wanted, i enabled single click again.
Removed CheckBox_PreviewMouseButtonDown event.
If you just want to toggle the checkbox status when double clicked, then simply make an CheckBox_MouseDoubleClick Event and set the IsChecked property to true. But take care, this Solution is not within the MVVM pattern.

Input gesture doesn't work wpf

Why doesn't my input gesture work in the following?
public class CustomRoutedUICommand : RoutedUICommand
{
private static RoutedUICommand _doSomethingCommand = null;
static CustomRoutedUICommand()
{
InputGestureCollection myInputs = new InputGestureCollection();
myInputs.Add(new KeyGesture(Key.G, ModifierKeys.Control | ModifierKeys.Shift));
_doSomethingCommand = new RoutedUICommand("DoSomething", "DoSomething", typeof(CustomRoutedUICommand), myInputs);
}
public static RoutedUICommand DoSomethingCommand { get { return _doSomethingCommand; } }
}
<Button Height="23" HorizontalAlignment="Left"
Command="{x:Static Control:CustomRoutedUICommand.DoSomethingCommand}"
CommandManager.CanExecute="Command_CanExecute" CommandManager.Executed="Command_Executed"
Content="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Command.Text}"
Margin="12,54,0,0" Name="Command" VerticalAlignment="Top" Width="Auto" Padding="2"/>
private void Command_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Hii");
}
In this case, the Command to work, need to have been an element logical focus, otherwise it will not work. You can specify KeyGesture a XAML way:
<Button Height="23" Content="Test" Name="Command" VerticalAlignment="Top"
Command="{x:Static Control:CustomRoutedUICommand.DoSomethingCommand}"
CommandManager.Executed="Command_Executed"
CommandManager.CanExecute="Command_CanExecute">
<Button.InputBindings>
<KeyBinding Command="{x:Static Control:CustomRoutedUICommand.DoSomethingCommand}" Gesture="CTRL+G" />
</Button.InputBindings>
</Button>
It works when the focus will be, it can be specified as follows:
Command.Focus();
To make your case to work, you need to use CommandBindings like this:
XAML
<Window x:Class="InputGestureHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Control="clr-namespace:InputGestureHelp"
WindowStartupLocation="CenterScreen"
ContentRendered="Window_ContentRendered"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="{x:Static Control:CustomRoutedUICommand.DoSomethingCommand}"
Executed="Command_Executed" CanExecute="Command_CanExecute" />
</Window.CommandBindings>
<Grid>
<Button Height="23" Content="Test" Name="TestButton"
VerticalAlignment="Top"
Command="{x:Static Control:CustomRoutedUICommand.DoSomethingCommand}" />
</Grid>
</Window>
Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Command_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Hii");
}
private void Command_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void Window_ContentRendered(object sender, EventArgs e)
{
TestButton.Focus();
}
}
public class CustomRoutedUICommand : RoutedUICommand
{
private static RoutedUICommand _doSomethingCommand;
static CustomRoutedUICommand()
{
InputGestureCollection myInputs = new InputGestureCollection();
myInputs.Add(new KeyGesture(Key.G, ModifierKeys.Control, "Ctrl + G"));
_doSomethingCommand = new RoutedUICommand("DoSomething", "DoSomething", typeof(CustomRoutedUICommand), myInputs);
}
public static RoutedUICommand DoSomethingCommand
{
get
{
return _doSomethingCommand;
}
}
}

Deleting dynamically created button from ListBox in WPF

I'm attempting to delete a dynamically created button from a listbox using the ListBox.Items.Remove, but I keep getting the error of "Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead." Problem is, ItemsControl.ItemsSource is not a valid option in my code.
A little run-down on the code: I have a MainWindow that contains a ListBox and the "Add" and "Delete" buttons. Adding a button sends you to a window where you can input a firstname and lastname. Clicking "Done" adds the newly-created profile's Button to the Listbox (you can access the profile by clicking on said button). I didn't include the Profile code as its empty except for the firstname and lastname being bound to labels there.
How would I access/modify the the button/profile in order to delete them? I know it has to do with the databinding, but I'm thoroughly confused on how to delete the item.
Any help would be much appreciated. I've included the MainWindow and ProfileCreator code below.
<Window x:Class="SavingButtons.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="UserTemplate">
<StackPanel Orientation="Horizontal">
<Button Name="TestAddButton" Click="TestAddButton_Clicked" Content="{Binding FirstName}" Width="100" Height="40"></Button>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Name="AddProfileButton" Content="Add Profile" HorizontalAlignment="Left" Margin="22,29,0,0" VerticalAlignment="Top" Width="75" Click="AddProfileButton_Click"/>
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
<Button Name="DeleteUserButton" Click="DeleteUserButton_Click" Content="Delete User" HorizontalAlignment="Left" Margin="246,69,0,0" VerticalAlignment="Top" Width="105"/>
</Grid>
namespace SavingButtons
{
public partial class MainWindow : Window
{
NewProfile np;
public int buttonNumberID;
public MainWindow()
{
InitializeComponent();
np = new NewProfile(this);
}
private void AddProfileButton_Click(object sender, RoutedEventArgs e)
{
np.Show();
}
//adds button to listbox
internal void TestAddButton_Clicked(object sender, RoutedEventArgs e)
{
Button cmd = (Button)sender;
if (cmd.DataContext is User)
{
//Profile is where the finished information is displayed//
Profile pro = new Profile();
pro.DataContext = cmd.DataContext;
pro.Show();
}
}
//this is where confusion ensues
private void DeleteUserButton_Click(object sender, RoutedEventArgs e)
{
//error occurs here
ButtonHoldersListbox.Items.Remove(ButtonHoldersListbox.SelectedItem);
}
}
}
The Profile Creator:
<Window x:Class="SavingButtons.NewProfile"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewProfile" Height="300" Width="500">
<Grid>
<Label Content="FirstName" HorizontalAlignment="Left" Margin="64,44,0,0" VerticalAlignment="Top"/>
<Label Content="LastName" HorizontalAlignment="Left" Margin="64,97,0,0" VerticalAlignment="Top"/>
<Button Name="UploadImageButton" Click="UploadImageButton_Click" Content="Upload Image" HorizontalAlignment="Left" Margin="64,146,0,0" VerticalAlignment="Top" Width="75"/>
<TextBox Name="FirstNameTextBox" HorizontalAlignment="Left" Height="23" Margin="126,47,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
<TextBox Name="LastNameTextBox" HorizontalAlignment="Left" Height="23" Margin="126,99,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
<Image Name="imgPhoto" HorizontalAlignment="Left" Height="100" Margin="173,146,0,0" VerticalAlignment="Top" Width="100"/>
<Button Name="ProfileFinishedLaunch" Content="Done" HorizontalAlignment="Left" Margin="360,232,0,0" VerticalAlignment="Top" Width="75" Click="ProfileFinishedLaunch_Click"/>
</Grid>
namespace SavingButtons
{
public partial class NewProfile : Window
{
public ObservableCollection<User> ProfileList;
public MainWindow mMain;
public NewProfile(MainWindow main)
{
InitializeComponent();
ProfileList = new ObservableCollection<User>();
mMain = main;
}
//loads image
private void UploadImageButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog op = new OpenFileDialog();
op.Title = "Select a picture";
op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png|" +
"JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
"Portable Network Graphic (*.png)|*.png";
if (op.ShowDialog() == true)
{
imgPhoto.Source = new BitmapImage(new System.Uri(op.FileName));
}
}
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
mMain.buttonNumberID++;
ProfileList.Add(new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source });
mMain.ButtonHoldersListbox.DataContext = ProfileList;
mMain.Show();
this.Hide();
}
You are setting yourListbox` to the others window property and you do it every time after a new item was added.
The error occurs, because the listbox items were set through binding to the ItemsSource property and in this case the ListBox.Items is read only so you can't remove or add item directly.
Instead of what you have now, add an ObservableCollection<User> property to your MainWindow class and bind the ListBox to this property. In the NewProfile window you need to add the new User item to this collection. The delete operation will work with removing the item from that collection (actually the senders DataContext)
public partial class MainWindow : Window
{
public ObservableCollection<User> Profiles {get; set;}
//...
private void DeleteUserButton_Click(object sender, RoutedEventArgs e)
{
var removable = ButtonHoldersListbox.SelectedItem as User;
if(removable != null)
Profiles.Remove(removable);
}
}
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Profiles}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
public partial class NewProfile : Window
{
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
mMain.buttonNumberID++;
var newUser = new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source };
mMain.Profiles.Add(newUser);
//Don't set the listbox.DataContext here
mMain.Show();
this.Hide();
}
if you set itemsource to usercontrol you can't operate it's items directly. Edit it's itemsource instead. give you a simple example.
public partial class MainWindow : Window
{
ObservableCollection<int> ProfileList;
public MainWindow()
{
InitializeComponent();
ProfileList = new ObservableCollection<int>();
this.DataContext = ProfileList;
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
Random r = new Random();
int num = r.Next(100);
ProfileList.Add(num);
//lstShow.Items.Add(num); error!
}
private void btnDel_Click(object sender, RoutedEventArgs e)
{
if (lstShow.SelectedIndex > -1)
{
ProfileList.Remove((int)lstShow.SelectedItem);
//lstShow.Items.Remove((int)lstShow.SelectedItem); error!
}
}
}
Thanks to Miklos, I did get my problem solved, however, the binding is still pretty confusing. Mainly: how does the ListBox know to bind the ObservableCollection ProfileList? In Mikalos version, he explicitly binds the ObservableCollection to the Listbox in the XAML(NOTE: Mikalos observable collection is named "Profile")
ItemsSource="{Binding Profiles}"
That would seem the most explicit. Instead, the only way I was only able to make it work was this way(ProfileList is the name I used for the observable collection):
ItemsSource="{Binding}"
Not sure how it knows to bind to my observableCollection. I will include my working code below.
<Window x:Class="SavingButtons.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="UserTemplate">
<StackPanel Orientation="Horizontal">
<Button Name="TestButton" Click="cmdDeleteUser_Clicked" Content="{Binding FirstName}" Width="100" Height="40"></Button>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Name="AddProfileButton" Content="Add Profile" HorizontalAlignment="Left" Margin="22,29,0,0" VerticalAlignment="Top" Width="75" Click="AddProfileButton_Click"/>
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
<Button Name="DeleteUserButton" Click="DeleteUserButton_Click" Content="Delete User" HorizontalAlignment="Left" Margin="246,69,0,0" VerticalAlignment="Top" Width="105"/>
</Grid>
My ProfileCreator Cs:
public partial class NewProfile : Window
{
public MainWindow mMain;
public NewProfile(MainWindow main)
{
InitializeComponent();
mMain = main;
}
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
////Mikalos CODE-----------------------------------------------------------//
var newUser = new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source };
mMain.ProfileList.Add(newUser);
mMain.ButtonHoldersListbox.DataContext = mMain.ProfileList;//Mikalo suggested not putting ListBox.DataContext here,
//however, this is the only place it works.
mMain.Show();
this.Hide();
//---------------------------------------------------------------//
}

How to bind a WPF control to the code behind?

I have this XAML:
<Window x:Class="WpfBindToCodeBehind.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
Loaded="Window_Loaded">
<StackPanel Orientation="Vertical">
<Button Name="ToggleExpand" Click="ToggleExpand_Click">Toggle Expander</Button>
<Expander Name="Expander"
Header="Don't click me, click the button!"
IsExpanded="{Binding RelativeSource={RelativeSource Self},Path=MayExpand}">
<TextBlock Text="{Binding}"/>
</Expander>
</StackPanel>
</Window>
This is the code behind:
public partial class Window1 : Window,INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Window1()
{
InitializeComponent();
}
private void ToggleExpand_Click(object sender, RoutedEventArgs e)
{
MayExpand = !mayExpand;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Expander.DataContext = "Show me";
}
private bool mayExpand;
public bool MayExpand
{
get { return mayExpand; }
set
{
mayExpand = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("MayExpand"));
}
}
}
The Binding expression for the IsExpanded property is not working. This code is a simplification, in reality the expander's binding is already set through the datacontent of a parent control.
How can I bind the IsExpanded property to a property of the code behind?
Can I bind it to the result of a method in the code behind?
The source for the binding is a RelativeSource.Self. That means the source is the Expander rather than the Window. Something like this will work:
IsExpanded="{Binding MayExpand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}"
You could also use a name to simplify things:
<Window x:Name="_root">
<Expander IsExpanded="{Binding MayExpand, ElementName=_root}"/>

How to access ListBox dynamically-created-items' properties from code-behind?

XAML:
<Window x:Class="WpfApp_ListBoxTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<ListBox Name="lb" Margin="0,0,0,70"></ListBox>
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,41" Name="btnAdd" VerticalAlignment="Bottom" Content="Add item" Width="75" Click="btnAdd_Click"></Button>
<TextBox Height="23" Margin="93,0,12,41" Name="txtInput" VerticalAlignment="Bottom" />
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,12" Name="btnGet" VerticalAlignment="Bottom" Content="Get value" Width="75" Click="btnGet_Click"></Button>
<TextBox Height="23" Margin="93,0,12,12" Name="txtReturn" VerticalAlignment="Bottom" IsReadOnly="True" />
</Grid>
</Window>
Csharp:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
namespace WpfApp_ListBoxTest
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
TextBox txt = new TextBox();
txt.Width = 200;
txt.Text = txtInput.Text;
lb.Items.Add(txt);
}
private void btnGet_Click(object sender, RoutedEventArgs e)
{
// What do I need to write here to get the value of the Text property of the selected TextBox?
}
}
}
And screenshot: (Sorry I'm not allowed to post picture directly)
http://i825.photobucket.com/albums/zz180/mGlushed/get_listbox_item_property.png
(In the picture above, I want to get the value "b" when I click the "Get value" button.)
I would like to know if there is a simple way to achieve this.
I'm new to WPF, so I only know to do this the long way, which is: Create an array. Everytime a new TextBox is created, add it into the array. Then access the TextBox'es through the array. But that doesn't sound very optimal, I think.
The 'WPF Way' of doing what you want is to use data binding:
Define a class with a string property called Text.
Create a collection of that class.
Bind your list box ItemsSource to the collection.
Create a DataTemplate that shows a TextBox with its Text property bound using {Binding Path=Text}.
In btnAdd_Click add an item to the collection (not directly to the ListBox)
In btnGet_Click you can get the text entered by casting ListBox.SelectedItem to your class and getting its Text property.
Example:
The simple class:
public class VMObject
{
public VMObject(string text)
{
Text = text;
}
public string Text { get; set; }
}
The window code-behind:
public partial class Window1 : Window
{
public ObservableCollection<VMObject> VM { get; set; }
public Window1()
{
VM = new ObservableCollection<VMObject>();
InitializeComponent();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
VM.Add(new VMObject(txtInput.Text));
}
private void btnGet_Click(object sender, RoutedEventArgs e)
{
if (lb.SelectedItem == null)
MessageBox.Show("No item is selected!");
txtReturn.Text = ((VMObject)lb.SelectedItem).Text;
}
}
The XAML:
<Window x:Class="lbtest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Window"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="TextBoxTemplate">
<TextBox Text="{Binding Path=Text}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Name="lb" Margin="0,0,0,70"
ItemsSource="{Binding ElementName=Window, Path=VM}"
ItemTemplate="{StaticResource TextBoxTemplate}" />
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,41"
Name="btnAdd" VerticalAlignment="Bottom"
Content="Add item" Width="75" Click="btnAdd_Click" />
<TextBox Height="23" Margin="93,0,12,41"
Name="txtInput" VerticalAlignment="Bottom" />
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,12"
Name="btnGet" VerticalAlignment="Bottom"
Content="Get value" Width="75" Click="btnGet_Click" />
<TextBox Height="23" Margin="93,0,12,12"
Name="txtReturn" VerticalAlignment="Bottom" IsReadOnly="True" />
</Grid>
</Window>
for a checkbox item:
private void chk_Checked(object sender, RoutedEventArgs e)
{
CheckBox chk = (CheckBox)sender;
MessageBox.Show(chk.Content.ToString());
}
No need for TextBox:s. ListBox handle strings fine.
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
// No need to create TextBox, ListBox handle strings fine.
lb.Items.Add(txtInput.Text);
}
private void btnGet_Click(object sender, RoutedEventArgs e)
{
// No selection, but button has been pressed.
if(lb.SelectedItem == -1)
return;
// Get selected item.
txtReturn.Text = (string)lb.SelectedItem;
/* If you change ListBox selection mode to multiple
* you can get all selected items by using foreach loop.
foreach (Object selected in lb.SelectedItems)
{
txtReturn.Text += (string) selected;
}
*/
}
If you just want to get the Text property of the selected TextBox (admiting your ListBox is in single selection mode) it it quite simple:
private void btnGet_Click(object sender, RoutedEventArgs e)
{
if(lb.SelectedItem != -1)
{
TextBox selectedTextBox = (TextBox)lb.SelectedItem;
txtReturn.Text = selectedTextBox.Text;
}
}
But if you want to implement the pretty WPF way, you should follow the Aviad P. solution, my solution do it well too.
Regards.
EDIT: If do not have a real need of TextBox functionalities, but only a string container, so follow Tuukka's solution.

Resources