WPF Databinding not calling ! - wpf

I have UserControl which contains a TextBox and a Button control. The Button opens a FileDialog and the user selects the file. The selected file is transferred into the FileName property which is a dependency property. For some reason the TextBox is not binding to this property. Here is the code:
<UserControl x:Class="WPF3D.FileInputBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" x:Name="root">
<Grid>
<StackPanel>
<TextBox Name="txtFile" Text="{Binding FileName, ElementName=root}" Width="300" Height="20" />
<Button Content="Select File" Width="100" Height="20" Click="SelectFile" />
</StackPanel>
</Grid>
</UserControl>
And here is the code for the UserControl.cs file:
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 Microsoft.Win32;
using System.Windows.Markup;
namespace WPF3D
{
[ContentProperty("FileName")]
public partial class FileInputBox : UserControl
{
public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register("FileName",
typeof (String),
typeof (FileInputBox));
public event EventHandler<EventArgs> FileNameChanged;
public FileInputBox()
{
InitializeComponent();
txtFile.TextChanged += new TextChangedEventHandler(txtFile_TextChanged);
}
void txtFile_TextChanged(object sender, TextChangedEventArgs e)
{
e.Handled = true;
if(FileNameChanged != null)
{
FileNameChanged(this, EventArgs.Empty);
}
}
public string FileName
{
get { return (string) GetValue(FileNameProperty); }
set { SetValue(FileNameProperty,value);}
}
private void SelectFile(object sender, RoutedEventArgs e)
{
// select the file
var fileDialog = new OpenFileDialog();
fileDialog.ShowDialog();
this.FileName = fileDialog.FileName;
}
protected override void OnContentChanged(object oldContent, object newContent)
{
if(oldContent != null)
throw new InvalidOperationException("You can't change the content");
}
}
}

I think this is just a scoping issue as evident by this output in the debug window:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=root'. BindingExpression:Path=FileName; DataItem=null; target element is 'TextBox' (Name='txtFile'); target property is 'Text' (type 'String')
If you just change it to this, it works fine:
<UserControl x:Class="TestApp.FileInputBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid x:Name="_grid">
<StackPanel>
<TextBox Name="txtFile" Text="{Binding FileName}" Width="300" Height="20" />
<Button Content="Select File" Width="100" Height="20" Click="SelectFile" />
</StackPanel>
</Grid>
</UserControl>
And the important part of the code-behind:
public FileInputBox()
{
InitializeComponent();
txtFile.TextChanged += new TextChangedEventHandler(txtFile_TextChanged);
_grid.DataContext = this;
}
Note that setting the DataContext on the Grid rather than the UserControl is intentional. If you do it at the UserControl level it becomes possible for consumers of your control to break your bindings simply by changing the DataContext of your UserControl.

Related

Contents of Content Control gone on tab switch

I am using mvvm. I am loading a usercontrol that contains a content control on two different tabs like so:
<TabControl>
<TabItem Header="View">
<StackPanel>
<Info:UserData/><!--UserData Control-->
<Button Content="View Entries" Command="{Binding BeginView}"/>
</StackPanel>
</TabItem >
<TabItem Header="Edit">
<StackPanel>
<Info:UserData/><!--UserData Control-->
<Button Content="Edit Entries" Command="{Binding BeginEdit}"/>
</StackPanel>
</TabItem >
</TabControl>
The User Control looks like:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{Binding UserTypeInfo}"/>
<Info:UserDetailsArea Grid.Row="1"/>
</Grid>
When the tab first loads the content of the ContentControl is set to an image. Depending on some actions the content may change to a datatable, video, etc. This part works fine.
When it loads the default tab is the first one. If I click on the second tab, you should see the same thing - with a different button, this works. But if I go back to the first tab the Content control is empty.
What do I need to do so that both tabs display the image?
The value bound to from the viewmodel as requested:
private object userTypeInfo
/// <summary>
/// User Specific data
/// </summary>
public object UserTypeInfo
{
get
{
return userTypeInfo;
}
private set
{
UuserTypeInfo= value;
OnPropertyChanged("UserTypeInfo");
}
}
Edit:
The following is a simplified example that I believe shows the same problem:
XAML code for window:
<Window x:Class="dualCC.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"
Loaded="Window_Loaded">
<Grid>
<TabControl>
<TabItem Header="One">
<StackPanel>
<Button Content="One" />
<ContentControl Name="CCone"/>
</StackPanel>
</TabItem>
<TabItem Header="Two">
<StackPanel>
<Button Content="Two" />
<ContentControl Name="CCtwo"/>
</StackPanel>
</TabItem>
</TabControl>
</Grid>
</Window>
Code behind (you'll need to fix the path to an image):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
namespace dualCC
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Uri uri = new Uri(#"C:\Image.jpg");
BitmapImage temp = new BitmapImage(uri);
Image CurrentImage = new Image();
CurrentImage.Source = temp;
CCone.Content = CurrentImage;
CCtwo.Content = CurrentImage;
}
}
}
This isn't MVVM. In MVVM you never manipulate GUI elements directly in code-behind like this.
To answer your question, the problem is that you're creating an Image, which is actually a child control, and setting it as the content of two separate controls. Controls can only have one parent. Create separate Images instead and set the BitmapImage as the source for each:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Uri uri = new Uri(#"C:\Image.jpg");
BitmapImage temp = new BitmapImage(uri);
CCone.Content = new Image { Source = temp };
CCtwo.Content = new Image { Source = temp };
}
Or better yet use proper MVVM and do it with data binding.

RadioButton is checked, it's Checked event is call when run the app, and report exception

There are two RadioButtons and two Labels in .xaml, and check one RadioButton make the corresponding Label Enable. RadioButton1 is checked . Run the app, and the Checked Event is called automatically, but Label is null, so the app report exception.
I just use a variable to mark whether the window is first created. Whether there are other methods? I don't know why the app run the events after initialize all components, who can tell me?
MainWindow.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="350"
Width="525">
<StackPanel>
<RadioButton
GroupName="Group"
IsChecked="True"
Content="RadioButton1"
x:Name="RadioButton1"
Checked="RadioButton1_Checked"
Unchecked="RadioButton1_Unchecked"/>
<RadioButton
GroupName="Group"
Content="RadioButton2"
x:Name="RadioButton2"
Checked="RadioButton2_Checked"
Unchecked="RadioButton2_Unchecked"/>
<Label
x:Name="Label1"
Content="Label1"/>
<Label
IsEnabled="False"
x:Name="Label2"
Content="Label2"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
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;
namespace WpfApplication7
{
/// <summary>
/// MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool isFirstRun;
public MainWindow()
{
isFirstRun = true;
InitializeComponent();
isFirstRun = false;
}
private void RadioButton1_Checked(object sender, RoutedEventArgs e)
{
if (isFirstRun)
return;
Label1.IsEnabled = true;
}
private void RadioButton2_Checked(object sender, RoutedEventArgs e)
{
Label1.IsEnabled = false;
}
private void RadioButton1_Unchecked(object sender, RoutedEventArgs e)
{
Label2.IsEnabled = true;
}
private void RadioButton2_Unchecked(object sender, RoutedEventArgs e)
{
Label2.IsEnabled = false;
}
}
}
I'm not sure if you have a specific reason for the EventHandlers, but if not you can do this logic directly in the Xaml using ElementName binding
Example:
<StackPanel>
<RadioButton x:Name="RadioButton1"
GroupName="Group"
IsChecked="True"
Content="RadioButton1" />
<RadioButton x:Name="RadioButton2"
GroupName="Group"
Content="RadioButton2" />
<Label x:Name="Label1"
IsEnabled="{Binding IsChecked, ElementName=RadioButton1}"
Content="Label1"/>
<Label x:Name="Label2"
IsEnabled="{Binding IsChecked, ElementName=RadioButton2}"
Content="Label2"/>
</StackPanel>
Why not simply remove the Ischecked="True" from the XAML and replace the isFirstRun = false variable being set after InitializeComponent(); with RadioButton1.IsChecked = true
this way you save having to define the isFirstRun variable.

silverlight binding combobox in nested controls

I have 2 user controls one named Filters and one named FilterItem
Filter looks like this:
<UserControl xmlns:my="clr-namespace:AttorneyDashboard.Views.UserControls" x:Class="AttorneyDashboard.Views.UserControls.Filters"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:helpers="clr-namespace:AttorneyDashboard.Helpers"
mc:Ignorable="d"
d:DesignHeight="150" d:DesignWidth="590" x:Name="FiltersRoot">
<Grid>
<ListBox x:Name="myListBox" ItemsSource="{Binding Path=FilterItems, ElementName=FiltersRoot}" >
<ListBox.ItemTemplate>
<DataTemplate>
<my:FilterItem ColumnsList="{Binding Path=Columns_, ElementName=FiltersRoot}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Diagnostics;
using AttorneyDashboard.Helpers;
namespace AttorneyDashboard.Views.UserControls
{
public class MyItems
{
public string Header { get; set; }
}
public partial class Filters : UserControl
{
public Filters()
{
InitializeComponent();
}
private DependencyProperty FilterItemsProperty = DependencyProperty.Register("FilterItems", typeof(ObservableCollection<FilterDescriptor>), typeof(Filters), new PropertyMetadata(null, new PropertyChangedCallback(OnChangeFilterItems)));
public ObservableCollection<FilterDescriptor> FilterItems
{
get
{
return (ObservableCollection<FilterDescriptor>)GetValue(FilterItemsProperty);
}
set
{
SetValue(FilterItemsProperty, value);
}
}
public static void OnChangeFilterItems(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public List<MyItems> Columns_
{
get
{
List<MyItems> list = new List<MyItems>();
list.Add(new MyItems() { Header = "test1" });
list.Add(new MyItems() { Header = "test2" });
list.Add(new MyItems() { Header = "test3" });
return list;
}
}
}
}
FilterItems looks like this
<UserControl x:Class="AttorneyDashboard.Views.UserControls.FilterItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="23" d:DesignWidth="590" xmlns:my="clr-namespace:AttorneyDashboard.Helpers" x:Name="FilterItemRoot">
<StackPanel Orientation="Horizontal">
<ComboBox Height="23" HorizontalAlignment="Left" Name="FieldName" VerticalAlignment="Top" Width="120" Margin="5,0,0,0" ItemsSource="{Binding Path=ColumnsList, ElementName=FilterItemRoot}" SelectedItem="{Binding PropertyPath, Mode=TwoWay}" DisplayMemberPath="Header"/>
</StackPanel>
</UserControl>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using AttorneyDashboard.Helpers;
using System.Windows.Data;
namespace AttorneyDashboard.Views.UserControls
{
public partial class FilterItem : UserControl
{
public FilterItem()
{
InitializeComponent();
}
private DependencyProperty ColumnsListProperty = DependencyProperty.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem), new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
public List<MyItems> ColumnsList
{
get
{
return (List<MyItems>)GetValue(ColumnsListProperty);
}
set
{
SetValue(ColumnsListProperty, value);
}
}
public static void OnChangeColumns(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
}
The number of FilterItems is ok (this means that FilterItems binding works ok), but only the Combobox of the last FilterItem is populated...
And I do not know what exactly is wrong...
Update:
I found why but I stll do not know a solution...
It seams that the content of FilterItem is binded before his properties are..
So the combobox in FilterItem is binded before the Columns property is binded...
Your code in FilterItem
private DependencyProperty ColumnsListProperty = DependencyProperty
.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem),
new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
Please, make it static:
private **static** DependencyProperty ColumnsListProperty = DependencyProperty
.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem),
new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
Thats it.
P.S. : in Filters make dependency property static too, generally do it anywhere :)
You have placed a x:Name attribute directly on your UserControl elements. Don't do that. Use this pattern instead:-
<UserControl x:Class="AttorneyDashboard.Views.UserControls.FilterItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="23" d:DesignWidth="590" xmlns:my="clr-namespace:AttorneyDashboard.Helpers" >
<StackPanel Orientation="Horizontal" x:Name="LayoutRoot">
<ComboBox Height="23" HorizontalAlignment="Left" Name="FieldName" VerticalAlignment="Top" Width="120" Margin="5,0,0,0" ItemsSource="{Binding Path=Parent.ColumnsList, ElementName=LayoutRoot}" SelectedItem="{Binding PropertyPath, Mode=TwoWay}" DisplayMemberPath="Header"/>
</StackPanel>
</UserControl>
You are not in control of name assigned to the user control, that belongs in the scope of the Xaml the uses your UserControl. If your code internally requires that the containing UserControl have a specific name then things are likely to break.

WPF DataContext ... looking for the simplest syntax

In the following XAML UserControl I am binding a few items to properties in the UserControl's linked class.
<UserControl x:Class="Kiosk.EventSelectButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Kiosk"
Height="130" Width="130">
<Grid>
<Button
Style="{DynamicResource DarkButton130x130}"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<Grid Margin="0,0,0,0" Height="118" Width="118">
<Image VerticalAlignment="Top" HorizontalAlignment="Center" Source="image/select_button_arrows.png" />
<Image x:Name="EventImageComponent" VerticalAlignment="Center" HorizontalAlignment="Center" Effect="{DynamicResource KioskStandardDropShadow}" Source="{Binding Path=EventImage}" />
<TextBlock x:Name="SelectTextBlock" Text="{Binding Path=SelectText}" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,-2,0,0" FontSize="10pt" Foreground="#5aaff5" />
<TextBlock x:Name="LabelTextBlock" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0,0,0,0" FontSize="14pt" FontWeight="Bold" Text="{Binding Path=Label}"/>
</Grid>
</Button>
</Grid>
</UserControl>
In the linked class' contstructor I'm applying the DataContext of the items to this, as you can see below.
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;
namespace Kiosk
{
/// <summary>
/// Interaction logic for EventSelectButton.xaml
/// </summary>
public partial class EventSelectButton : UserControl
{
public String ValueContainer;
private String _EventImage;
public String EventImage
{
get
{
return _EventImage;
}
set
{
_EventImage = value;
}
}
private String _Label;
public String Label
{
get
{
return _Label;
}
set
{
_Label = value;
}
}
private String _SelectText;
public String SelectText
{
get
{
return _SelectText;
}
set
{
_SelectText = value;
}
}
public EventSelectButton()
{
InitializeComponent();
LabelTextBlock.DataContext = this;
SelectTextBlock.DataContext = this;
EventImageComponent.DataContext = this;
}
}
}
Edit
Although this works as intended, I'm interested to know if there is a simpler way of doing this. (edit, lessons learned.) This won't actually work beyond the initialisation, the public properties will be set, however because the class doesn't use DependentProperties or alternatively, implement INotifyPropertyChanged, binding will not work as expected. (end edit)
For example,
Can I set the DataContext of these items in the XAML to this (as the EventSelectButton instance), and if so, how?
Alternatively, is it possible to inherit the DataContext from the UserControl parent, thus making the Binding Paths simpler.
The only alternatives I've found so far are more verbose, e.g. using the RelativeSource binding method to locate the EventSelectButton Ancestor.
So please, let me know any ways I can improve this binding expression, and any comments on best practices for binding within a UserComponent are much appreciated.
One way is to do the following:
Name your UserControl in your XAML.
Bind the DataContext of the root element (i.e. Grid) to the UserControl.
Like this:
<UserControl x:Name="uc">
<Grid DataContext="{Binding ElementName=uc}">
.
.
.
</Grid>
</UserControl>
Now, you'll ask, why not just set the DataContext of the UserControl itself? Well, this just ensures that setting the DataContext of an instance of the UserControl will still work without affecting the bindings in the UserControl's visual tree. So something like the one below will still work fine.
<Window>
<uc:EventSelectButton DataContext="{Binding SomeDataContext}" Width="{Binding SomeDataContextWidth}"/>
</Window>
EDIT
To make the solution complete requires the properties in the UserControl to be changed to use DependencyProperty objects instead. Below are the updates to the codes:
XAML:
<UserControl x:Class="Kiosk.EventSelectButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Kiosk"
x:Name="root"
Height="130" Width="130">
<Grid x:Name="LayoutRoot" DataContext="{Binding ElementName=root}">
<Button
Style="{DynamicResource DarkButton130x130}"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<Grid Margin="0,0,0,0" Height="118" Width="118" >
<Image VerticalAlignment="Top" HorizontalAlignment="Center" Source="image/select_button_arrows.png" />
<Image Source="{Binding EventImage}" VerticalAlignment="Center" HorizontalAlignment="Center" Effect="{DynamicResource KioskStandardDropShadow}" />
<TextBlock Text="{Binding SelectText}" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,-2,0,0" FontSize="10pt" Foreground="#5aaff5" />
<TextBlock Text="{Binding Label}" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0,0,0,0" FontSize="14pt" FontWeight="Bold" />
</Grid>
</Button>
</Grid>
</UserControl>
Code-Behind:
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;
namespace Kiosk
{
public partial class EventSelectButton : UserControl
{
public static readonly DependencyProperty EventImageProperty =
DependencyProperty.Register(
"EventImage",
typeof(string),
typeof(EventSelectButton));
public String EventImage
{
get { return (string)GetValue(EventImageProperty); }
set { SetValue(EventImageProperty, value); }
}
public static readonly DependencyProperty SelectTextProperty =
DependencyProperty.Register(
"SelectText",
typeof(string),
typeof(EventSelectButton));
public String SelectText
{
get { return (string)GetValue(SelectTextProperty); }
set { SetValue(SelectTextProperty, value); }
}
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register(
"Label",
typeof(string),
typeof(EventSelectButton));
public String Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public EventSelectButton()
{
InitializeComponent();
}
}
}

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