WPF Combobox based databinding Textbox - wpf

I have been trying to find a solution to this one but could not find a relevant answer online.
I have a bunch of text boxes that I need to populate with data, for example in my case address. I have a combo box that is bound to an enum and has a list of values to select from
Home, Office, MainOffice, Laboratory. etc. When I make a selection on combobox, I need to populate its address in the text boxes below. I can get Home address from object X, Office Address from Object Y, MainOffice from Z. How do I do this conditional databinding using combobox selection. Please advise.
These are the options that I could select
public enum MailToOptions
{
Home,
Office,
CorporateOffice,
Laboratory,
}
<!-- Row 1 -->
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="5" Content="Mail To:" />
<ComboBox Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="5" Name="MailToComboBox"
ItemsSource="{Binding Source={StaticResource odp}}"
SelectionChanged="HandleMailToSelectionChangedEvent" >
</ComboBox>
<!-- Agency ID -->
<!-- Name -->
<Label Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="5" Content="Name" />
<TextBox Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="5">
<TextBox.Text>
<Binding Path="Order.Agency.PartyFullName" Mode="TwoWay" />
</TextBox.Text>
</TextBox>
<!-- Row 2 -->
<!-- Address Line 1 -->
<Label Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="5" Content="Address 1" />
<TextBox Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="5">
<TextBox.Text>
<Binding Path="Order.Agency.AddressLine1" Mode="TwoWay" />
</TextBox.Text>
</TextBox>

The best way would be to make the items in your ComboBox objects, and bind your text fields to the ComboBox.SelectedItem
For example,
<ComboBox x:Name="AddressList" ItemsSource="{Binding Addresses}" DisplayMemberPath="Name" />
<TextBox Text="{Binding SelectedItem.Street, ElementName=AddressList}" ... />
<TextBox Text="{Binding SelectedItem.City, ElementName=AddressList}" ... />
<TextBox Text="{Binding SelectedItem.State, ElementName=AddressList}" ... />
<TextBox Text="{Binding SelectedItem.ZipCode, ElementName=AddressList}" ... />
A cleaner way would be to set the DataContext of whatever panel holds the TextBoxes
<ComboBox x:Name="AddressList" ItemsSource="{Binding Addresses}" DisplayMemberPath="Name" />
<Grid DataContext="{Binding SelectedItem, ElementName=AddressList}">
<TextBox Text="{Binding Street}" ... />
<TextBox Text="{Binding City}" ... />
<TextBox Text="{Binding State}" ... />
<TextBox Text="{Binding ZipCode}" ... />
</Grid>
You can either bind the ComboBox to something like an ObservableCollection<Address>, or set the ItemsSource manually in the code behind.
I'd recommending binding, however to set it manually in the code behind it would look something like this:
var addresses = new List<Addresses>();
addresses.Add(new Address { Name = "Home", Street = "1234 Some Road", ... });
addresses.Add(new Address { Name = "Office", Street = "1234 Main Street", ... });
...
AddressList.ItemsSource = addresses;

Related

Link textbox to combobox - not working

I have a WPF usercontrol with a combobox & textbox. I want the textbox to hold the value of the selected item in the combobox and it works fine if I use SelectedValue in the binding path. However if I try to use the Title column of the combobox (SelectedValue.Title) the value of the textbox is overwritten but nothing is displayed. Can anyone tell me what I am doing wrong? My code sample is below. I am a newbie at this so please be kind :)
<ComboBox x:Name="ComboProject" Grid.Column="4" Grid.Row="0" TabIndex="14"
ItemsSource="{Binding Source={StaticResource Projects}, XPath=./Project}"
SelectedValuePath="#Item"
Tag="Project Number"
TextSearch.TextPath="#Item">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text= "{Binding XPath= #Item}" Width="90" />
<TextBlock Text= "{Binding XPath= #Title}" Width="220" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox x:Name="loaded" Text="{Binding Path=SelectedValue.Title, NotifyOnSourceUpdated=True, ElementName=ComboProject}" Grid.Row="2" Grid.Column="4" Tag="Project Title" TabIndex="15"/>
You set SelectedValuePath="#Item", so that's what SelectedValue has right now. Try setting it to Title and binding directly to SelectedValue:
<ComboBox x:Name="ComboProject"
ItemsSource="{Binding Source={StaticResource Projects}, XPath=./Project}"
SelectedValuePath="#Title">
<ComboBox.ItemTemplate>
...
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Text="{Binding SelectedValue, ElementName=ComboProject}" />
I removed some other code for clarity of example.
EDIT :
Ok, if you want to use SelectedValue for other purposes we can bind TextBox to SelectedItem instead. If the Title is an attribute of a selected XML node, then we can access it like this:
<TextBox Text="{Binding SelectedItem.Attributes[Title].Value, Mode=OneWay, ElementName=ComboProject}" />

Define ComboBox' DataTemplate for TextBox (and define search pattern) when IsEditable is True?

I have a ComboBox whose ItemsSource is binding to a collection and the SelectedItem is binding to the properties of my VieModel. Let's call the binding properties AvailableOptions and TargetOption in ViewModel. And type of collection item and TargetOption is called MyOption. I have such requirements but I don't know how to fulfill them all:
It should be OK for the binding TargetOption to have NULL as value
I want to set a DataTemplate for the target type in TargetOption collection to be displayed in the ComboBox
If possible, I want use different DataTemplate for MyOption when then are in the drop-down of ComboBox and when one item is selected. Because my UserControl has limited space so it should be compact when item is selected and during the selection it should provide more information
As I said, I don't know how to do all of them. At first I have the XAML like this:
<ComboBox SelectedItem="{Binding SelectedOption} ItemsSource="{Binding AvailableOptions}" >
<ComboBox.ItemTemplateSelector>
<MyNameSpace:ComboBoxItemTemplateSelector ItemTemplate="{StaticResource OptionDetailTemplate}" SelectedItemTemplate="{StaticResource OptionSimpleTemplate}" />
</ComboBox.ItemTemplateSelector>
</ComboBox>
With a customized ItemTemplateSelector. I am able to do the requirement 2) and 3). My OptionDetailTemplate looks like this:
<DataTemplate x:Key="OptionDetailTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ShortName}" />
<TextBlock Text=" | " />
<TextBlock Text="{Binding Code}" />
</StackPanel>
</DataTemplate>
and OptionSimpleTemplate looks like this:
<DataTemplate x:Key="OptionSimpleTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ShortName}" />
<TextBlock Text=" | " />
<TextBlock Text="{Binding Code}" />
<TextBlock Text=" | " />
<TextBlock Text="{Binding Number}" />
</StackPanel>
</DataTemplate>
But now the problem is requirement 1). When the user select one option from the drop down list of the ComboBox, he can't put it as NULL back which should be allowed. This is because AvailableOption doesn't have a NULL object
I see that if I set IsEditable to True for the ComboBox, and set TextSearch.TextPath to Code, it allows the text quick search/assign and also allows to have a NULL value when the search text is totally deleted. But now when I ever select one, it only displays Code (the OptionTemplate does not have any effect any more because now it displays the selected item in a TextBox). This is not good since only Code is not enough for the user to tell what Option it is. But since I have multiple properties in MyOption class, how can I define the DataTemplate for the TextBox and also define the search routine?
I have to be honest that I didn't fully understand your first requirement and its ramifications. however, I am really just answering to let you know that you don't even need to use a DataTemplateSelector to select between your two DataTemplates. If you do not set the x:Key value on them, then they will be applied to the relevant items implicitly:
<DataTemplate DataType="{x:Type YourXamlNamespacePrefix:TargetOption}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ShortName}" />
<TextBlock Text=" | " />
<TextBlock Text="{Binding Code}" />
</StackPanel>
</DataTemplate>
...
<DataTemplate DataType="{x:Type YourXamlNamespacePrefix:MyOption}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ShortName}" />
<TextBlock Text=" | " />
<TextBlock Text="{Binding Code}" />
<TextBlock Text=" | " />
<TextBlock Text="{Binding Number}" />
</StackPanel>
</DataTemplate>
Furthermore, you could do all this data binding with just a single TextBlock if you use a MultiDataTrigger:
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}|{1}">
<Binding Path="ShortName" />
<Binding Path="Code" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Perhaps if you try clarifying your remaining problem (in your question), I might understand?

How to override parent datacontext in child panel

I want to override parent (grid) datacontext in one of the child panel (stackpanel),
such that any update in any of the stackpanel, xml file will get updated accordingly.
Initially I had XPath="/Data/MB" attribute in XmlDataProvider. So with this binding in only textbox 5 and 6 was successful.
Finally what I did is given below. This works fine.
<Grid>
<Grid.DataContext>
<XmlDataProvider x:Name="Credentials" Source="Credentials.xml"/>
</Grid.DataContext>
<StackPanel>
<TextBox Height="23" Name="textBox5" Width="188" Text="{Binding XPath=/Credentials/MessageBroker/Hostname}" />
<TextBox Height="23" Name="textBox6" Width="188" Text="{Binding XPath=/Credentials/MessageBroker/Port}"/>
</StackPanel>
<StackPanel>
<TextBox Height="23" Name="textBox9" Width="188" Text="{Binding XPath=/Credentials/Database/Server}" />
<TextBox Height="23" Name="textBox10" Width="188" Text="{Binding XPath=/Credentials/Database/Password}"/>
</StackPanel>
</Grid>
Here is Credentials.xml
<?xml version="1.0" encoding="utf-8"?>
<Credentials>
<MB>
<Hostname>145.111.227.222</Hostname>
<Port>5672</Port>
<UserName>Admin</UserName>
<Password>Admin</Password>
</MB>
<Database>
<Server>145.111.227.234</Server>
<UserID>Administrator</UserID>
<Password>password</Password>
</Database>
Is there any better way to do it?
Please let me know.

Binding multiple Sources to a ListView

I have a ListView with a dataTemplate which I need to bind to 3 different sources with the same index. I think I have to do this completely in XAML, because the sources (chart) only exists in xaml. I'm using the MVVM Pattern."
I have wrote down how it "should" work, the index i is the common key.
<ListView ItemsSource="{Binding ???}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<!-- Small rectangle filled with the same color as the corresponding line -->
<Rectangle
Height="10"
Width="10"
Fill="{Binding ElementName=chart, Path=Series[i].LineStroke}" />
<!-- The title of the corresponding line -->
<TextBlock
x:Name="Title"
Text="{Binding ElementName=chart, Path=Series[i].DataSeries.Title}" />
<!-- The actual value of the corresponding line on the current position-->
<TextBlock
x:Name="Value"
Text="{Binding ElementName=chart, Path=Behaviour.Behaviours[0].CurrentPoints[i].Y}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Mh, lets see. How about you bind you listview to chart.Series this way you get the right number of elements. An then in your data template you bind the properties of the series. For the behaviour you could use MultiBinding and a converter to extract the data
<ListView ItemsSource="{Binding Path=Series, ElementName=chart}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<!-- Small rectangle filled with the same color as the corresponding line -->
<Rectangle
Height="10"
Width="10"
Fill="{Binding Path=LineStroke}" />
<!-- The title of the corresponding line -->
<TextBlock
x:Name="Title"
Text="{Binding Path=DataSeries.Title}" />
<!-- The actual value of the corresponding line on the current position-->
<TextBlock x:Name="Value">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource ChartSeriesBehaviourConverter}">
<Binding ElementName=chart/>
<Binding/>
<MultiBinding>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now you should get the chart and the current series object passed into your converter where you should be able to do something like var idx = chart.Series.IndexOf(s) so you can access the corresponding point in the behaviours.

Dataform fields won't appear

I am trying to learn how to use the Silverlight 3 DataForm control, because I need to define the DataForm fields myself in the XAML code, that is, I don't want to use the AutoGenerateFields property.
My problem is: the dataform works perfectly when the AutoGenerateFields is set to true, but when I create a DataForm and set the fields manually and run the application, all I get is an empty blank rectangle where my form and its fields should be.
I created a blank Silverligh Navigation Application to test this, and below is the code of the Home.xaml page:
<Grid x:Name="LayoutRoot">
<StackPanel>
<!-- This doesn't work. It renders a blank rectangle -->
<dataFormToolkit:DataForm x:Name="DataForm">
<dataFormToolkit:DataForm.EditTemplate>
<DataTemplate>
<StackPanel dataFormToolkit:DataField.IsFieldGroup="True">
<dataFormToolkit:DataField>
<TextBox Text="Test1" />
</dataFormToolkit:DataField>
<dataFormToolkit:DataField>
<TextBox Text="Test2" />
</dataFormToolkit:DataField>
<dataFormToolkit:DataField>
<TextBox Text="Test3" />
</dataFormToolkit:DataField>
</StackPanel>
</DataTemplate>
</dataFormToolkit:DataForm.EditTemplate>
</dataFormToolkit:DataForm>
<!-- This works. -->
<dataFormToolkit:DataForm x:Name="DataForm2"/>
</StackPanel>
</Grid>
To make the second DataForm work, I simply created a Person class, and put the following in Home.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Person client = new Person { Age = 10, DateOfBirth = new DateTime(1980, 10, 20), FirstName = "John", LastName = "Doe" };
DataForm2.CurrentItem = client;
}
You can see what happens when I run the application:
Does anyone know what's wrong? Thank you in advance.
To get something to appear I had to add:
DataForm.CurrentItem = client;
to the code.
This just displayed three text boxes with no labels and the entries "Test1", "Test2" and "Test3". Is this what you were expecting?
The Silverlight Toolkit samples page has an example of a template driven data form and it's XAML looks like this:
<dataform:DataForm x:Name="dataForm" ItemsSource="{Binding}" HorizontalAlignment="Left" MinWidth="400" MaxWidth="500" Margin="4" Grid.Column="1">
<dataform:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<dataform:DataField>
<TextBox Text="{Binding FirstName, Mode=TwoWay}" />
</dataform:DataField>
<dataform:DataField>
<TextBox Text="{Binding Email, Mode=TwoWay}" />
</dataform:DataField>
<dataform:DataField>
<TextBox Text="{Binding Phone, Mode=TwoWay}" />
</dataform:DataField>
<dataform:DataField Label="Calendar">
<controls:Calendar></controls:Calendar>
</dataform:DataField>
</StackPanel>
</DataTemplate>
</dataform:DataForm.EditTemplate>
</dataform:DataForm>
And there's the line:
DataContext = Contact.People;
in the code behind. (The class People is defined elsewhere as far as I can work out)
I also found it quite surprising that you need to bind to something before the form will even appear.
If you're trying to bind to a single item you need to do this :
CurrentItem="{Binding Customer}"
or - if you're in a user control, just
CurrentItem="{Binding}"
and then in the parent control
<my:AddressControl DataContext="{Binding Customer}"/>
Here's the full dataform:
<dt:DataForm Name="dfAddress" AutoGenerateFields="False" CurrentItem="{Binding}">
<dt:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<dt:DataField Label="First Name">
<TextBox Text="{Binding FirstName, Mode=TwoWay}" Style="{StaticResource FieldTextBoxStyle}" HorizontalAlignment="Stretch" IsReadOnly="False" HorizontalContentAlignment="Stretch" />
</dt:DataField>
<dt:DataField Label="Last Name">
<TextBox Text="{Binding LastName, Mode=TwoWay}" Style="{StaticResource FieldTextBoxStyle}" HorizontalContentAlignment="Stretch" />
</dt:DataField>
</StackPanel>
</DataTemplate>
</dt:DataForm.EditTemplate>
</dt:DataForm>
You could give the CurrentItem="" in xaml. In this way, you dont need to actual bind it to anything and at the same time, work on the dataform looks :)

Resources