How to bind string property to TextBox within ListBox - c#

I'm binding a List<string> to my ListBox in WPF using MVVM
At the moment I have
<ListBox ItemsSource="{Binding FileContents}"></ListBox>
File Contents in my ViewModel is simply
public List<string> FileContents {get;set;}
And the FileContents values are set in the constructor of the ViewModel, as such there is no need to worry about INotifyProperty
Everything works fine so far. I can see the list displayed in my ListBox as desired.
Now I need to provide a template! This is where it goes wrong
<ListBox ItemsSource="{Binding FileContents}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is where it all goes wrong! My understanding is that I only need to do <TextBox Text = "{Binding}" because the ListBox is already bound to the List<string> property (called FileContents)
However, when I run the above Visual Studio gives me
The application is in break mode
If I update the code to
<TextBox Text = "Some String Value"
then it works fine
I don't understand what I've done wrong.

Set the Mode of the Binding to OneWay:
<TextBox Text="{Binding Path=., Mode=OneWay}" />
The default binding mode for the Text property of a TextBox is TwoWay but this won't work when you bind to a string in a List<string>.

Binding to a string directly is only possible one way. This means you are only able to bind read only like
<TextBox Text="{Binding Mode=OneWay}"/>
or
<TextBox Text="{Binding .}"/>
The reason is simple: Changing the string means you are removing and adding an item to your list. This is simply not possible by changing the string in a TextBox.
A solution is to wrap the content in a class like
public class FileContent
{
public string Content { get; set; }
}
and bind to a list of List<FileContent> by using <TextBox Text="{Binding Content}"/> as template.

Related

Binding Property of an Item in an ItemsControl From a Collection

My theory code:
ScriptContainerUserControl.xaml
<ItemsControl x:Name="ScriptItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox x:Name="pTB" Text="{Binding PhasePriority}" />
<TextBox x:Name="nTB" Text="{Binding Name}" />
<TextBox x:Name="dTB" Text="{Binding Description}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ScriptContainerUserControl.xaml.cs
public ScriptContainerUserControl() : base()
{
InitializeComponent();
ScriptItemsControl.ItemsSource = PScriptCollection;
}
//PScriptCollecion is of type SynchronizedObservableCollection<ProcessScript>
//ProcessScript has the elements PhasePriority, Name, and Description
Would the code above work for making sure
ScriptItemsControl[i].dTB.Text = PScriptCollection[i].Description?
Or is it not possible to bind like this?
Fenster,
It should definitely work, provided you have getter setter properties implemented for all the three properties in ProcessScript class.
When you use a datatemplate - it means you are setting the datacontext of each element of your itemscontrol to an element of your collection.
so here each Itemcontrol element will look at ProcessScript object and if that object has all three properties , you should see the data.
It is not possible to do it in this way. You do not set Binding actually... To have support for observing a changes on collection you should bind the collection to ItemsSource property of ItemsControl.
Instead of line:
ScriptItemsControl.ItemsSource = PScriptCollection;
try this
ScriptItemsControl.ItemsSource = new Binding("PScriptCollection");

Different datasource on one item in ListBox

I have a ListBox bound to a data context that works fine. But on some items I want to show a textblock with data from another context. But I cant get it to work. I had it working on a string in the cs class, but when I changed it to a class that implements INotifyPropertyChanged i dont get the text to show at all...
This is a simplified ItemTemplate of my xaml:
<DataTemplate x:Key="ArrivalFlightItemTemplate">
<TextBlock Text="{Binding ArrivalFlightItem.Operator}" />
<StackPanel Visibility="{Binding Converter={StaticResource FlightDisclaimerConverter}}">
<TextBlock Text="{Binding DisclaimerText}" Style="{StaticResource NormalText}" Foreground="{StaticResource DarkForegroundColor}">
<TextBlock.DataContext>
<local:FlightDisclaimerItem/>
</TextBlock.DataContext>
</TextBlock>
</StackPanel>
</DataTemplate>
In the cs constructor I have:
var flightsVM = SingletonClass.FlightsViewModel;
this.DataContext = listFlightsVM;
disclaimerItem = new FlightDisclaimerItem();
disclaimerItem.DisclaimerText = Strings.FlightInfoDisclaimerShort;
Can anyone please help me figure this out (I'm new to Windows Phone)?
Why are you binding some of your data in your code-behind? You should do it all in XAML.
Define disclaimerItem as a property in your ViewModel.
Set the name for your PhoneApplicationPage
Use the PhoneApplicationPage Name in your markup extension to bind the textblock in question to the ViewModel property
<TextBlock DataContext="{Binding DataContext.DisclaimerItemProperty, ElementName=phoneApplicationPageName}" />
I got it working! I couldnt get it to work using another DataContext in the DataTemplate. But by binding to another object in the page (disclaimer panel) it works as intended.
Text="{Binding ElementName=DisclaimerPanel, Path=DataContext.DisclaimerText}"

WPF/C# Binding custom object list data to a ListBox?

I've ran into a bit of a wall with being able to bind data of my custom object list to a ListBox in WPF.
This is the custom object:
public class FileItem
{
public string Name { get; set; }
public string Path { get; set; }
}
And this is the list:
private List<FileItem> folder = new List<FileItem>();
public List<FileItem> Folder { get { return folder; } }
The list gets populated and maintained by a FileSystemWatcher as files get moved around, deleted, renamed, etc. All the list does is keeps tracks of names and paths.
Here's what I have in the MainWindow code-behind file (it's hard coded for testing purposes for now):
FolderWatcher folder1 = new FolderWatcher();
folder1.Run(#"E:\MyApp\test", "*.txt");
listboxFolder1.ItemsSource = folder1.Folder;
Here's my XAML portion:
<ListBox x:Name="listboxFolder1" Grid.Row="1" BorderThickness="0"
ItemsSource="{Binding}"/>
Unfortunately, the only thing that gets displayed is MyApp.FileItem for every entry. How do I display the specific property such as name?
You will need to define the ItemTemplate for your ListBox
<ListBox x:Name="listboxFolder1" Grid.Row="1" BorderThickness="0"
ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The easiest way is to override ToString on your FileItem, (The listbox uses this to populate each entry)
public override string ToString()
{
return Name;
}
Each item in the list that ListBox shows automatically calls the ToString method to display it, and since you didn't override it, it displays the name of the type.
So, there are two things you can do here.
Override the ToString method like Sayse suggested.
Use DataTemplate and bind each of your properties seperatly
In your resource add the template with a key
<DataTemplate x:Key="fileItemTemplate">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Path}"/>
</StackPanel>
</DataTemplate>
and give it as your listbox ItemTemplate
<ListBox x:Name="listboxFolder1" Grid.Row="1" BorderThickness="0" ItemsSource="{Binding}" ItemTemplate="{StaticResource fileItemTemplate}">
In case anyone comes across this now via search, I just encountered pretty much the same issue in a C# UWP app.
While the XAML bits in Nitin's answer above were necessary, they didn't fix the issue alone -- I also had to change my equivalent of Folder to be an ObservableCollection, rather than a List, to get the ListBox to show the property I needed.

WPF, Datatemplates, and Data binding

Not a specific code question of any sort, I'm just looking to better understand exactly how data binding works in a DataTemplate. Here's just an example block of code; I have defined a Client class with three attributes (the purpose of these attributes is irrelevant to the question)
public class Client
{
public bool Powered { get; set; }
public bool clientAlive { get; set; }
public bool updaterAlive { get; set; }
}
I populate a ListView using a list of clients:
List<Client> clientList = new List<Client>();
//populate the list from JSON url, code omitted
listView1.ItemsSource = clientList;
And here's the block of XAML code that holds the template for displaying the items in the ListView:
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Powered: " FontWeight="Bold" />
<TextBlock Text="{Binding Powered}" />
<TextBlock Text=", " />
<TextBlock Text="clientAlive: " FontWeight="Bold" />
<TextBlock Text="{Binding clientAlive}" />
<TextBlock Text=", " />
<TextBlock Text="updaterAlive: " FontWeight="Bold" />
<TextBlock Text="{Binding updaterAlive}" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
The code runs fine and everything displays as expected, I was just wondering if anyone could explain how data binding in WPF works. As far as I'm concerned, there's nothing in the XAML that references the Client class and I'm just confused as to how the XAML knows to display the property the binding specifies. Does the Text = "{Binding = Powered}" just look for an attribute that matches the binding within the item type that populates the list?
Does the Text = "{Binding = Powered}" just look for an attribute that matches the binding within the item type that populates the list?
Basically, yes. If the item that your populated the list with didn't have that attribute, you would see binding errors (look in the console while debugging).
You can also supply a type to your DataTemplate which will allow you to have multiple templates that will be applied depending on the specific type of the object in your collection.
When the collection get bound to the listview, each list item container will be generated with Content set to Client object. So the visual present inside the data template has Client object as its data context.
The line "{Binding Powered}" will look up the datacontext and find the property named "Powered" and resolve its value. Just remove the word "Powered" and leave it as "{Binding}", WPF will display the fully qualified name of your datacontext object.

How to get textblock binding value?

I had bind my textblock in xaml, is it possible to get the value out into my coding?
My coding for binding
<TextBlock Height="40" HorizontalAlignment="Left" Margin="8,24,10,0" Name="txtBlockCustName" Text="{Binding CustName, Mode=OneWay}" VerticalAlignment="Top" FontSize="26" />
I want to put in my mainpage.xaml.cs like
string CustName = txtBlockCustName.Text;
but it had error on it..
You can't access this textblock because it is bound in a listboxtemplate. If there are multiple textblocks in the list, how can you access it by name? The program won't know what textblock you are asking for. This is why an error is thrown.
You could use the collection that you bound to the listbox to get the customer name.

Categories