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 :)
Related
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}" />
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;
I've used the AutoCompleteBox without problem on a WPF form. Now I would like to do the same thing inside a WPF DataGrid. Almost everything works except the setter for SelectedItem. I see the getter get called but after typing a value and hitting tab (or using the arrow keys) the setter never gets called. In the console output I see no binding errors. I'm hoping someone can tell me what I'm doing wrong and how to get SelectedItem to fire the setter on the property in ViewModel class when it's inside a DataGrid. First the snippet of the ViewModel class:
public static List<ImpaSimple> AllImpas { get { return ImpaListRepository.ImpaList; } }
private ImpaSimple _selectedImpa;
public ImpaSimple SelectedImpa
{
get { return _selectedImpa; }
set
{
if (value == _selectedImpa) return;
_selectedImpa = value;
//Manually set Description and Unit fields because user can override the IMPA default values.
// Description = _selectedImpa.Name;
//TODO Set Units too
RaisePropertyChanged("SelectedImpa");
}
}
The XAML
In the XAML below I have added a code behind handler for LostFocus as a temporary work-around. The addition of the UpdateSourceTrigger attribute was also an attempt to get this working.
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<TextBlock Style="{StaticResource DataGridHeader}">LImpa</TextBlock>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Hots:AutoCompleteBoxEx ToolTip="Start typing an IMPA number"
ItemsSource="{Binding AllImpas}"
Width="50"
HorizontalContentAlignment="Left"
FilterMode="StartsWith"
IsDropDownOpen="True"
IsTextCompletionEnabled="True"
LostFocus="ImpaBoxExLostFocus"
SelectedItem="{Binding SelectedImpa,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<Hots:AutoCompleteBoxEx.ItemTemplate>
<DataTemplate>
<Grid Width="450">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="275" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ImpaId}"
Grid.Column="0" />
<TextBlock Text="{Binding Name}"
Grid.Column="1" />
<TextBlock Text="{Binding Unit}"
Grid.Column="2" />
</Grid>
</DataTemplate>
</Hots:AutoCompleteBoxEx.ItemTemplate>
</Hots:AutoCompleteBoxEx>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
Ah, I think I know what that is - the defect I call 'shy datacontext' - try setting your
Hots:AutoCompleteBoxEx DataContext to:
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type YourDataSourceItemType}}}"
The way to check it is create a dummy converter and use it like that:
ItemsSource="{Binding Converter={StaticResource DummyConverter}}"
then put a breakpoint inside its Convert and check for the value. Since no Path is specified - the input value is the DataContext itself, if it's null, then it never gets set/got lost.
I have a form:
<StackPanel Orientation="Horizontal" Visibility="{Binding Editable, Converter={StaticResource visibilityConverter}}"
ToolTipService.ToolTip="Add new topic to this group">
<sdk:AutoCompleteBox Width="160" ItemsSource="{Binding ElementName=LayoutRoot, Path=DataContext.TopicNames}" />
<Button Click="addTopicButton_Click">
<Image Source="Images/appbar.add.rest.png" />
</Button>
</StackPanel>
This form appears in a DataTemplate for an ItemsControl. I'm not sure what the best way is to get the data from the AutoCompleteBox when the button is clicked. I can't give the elements x:Name attributes, because they're in a template (right?).
How can I get around this? The Click event will give me the Button, but I need a reference to the text box. Use the Button's parent, then look through the children for the Textbox? If I factored this out into its own UserControl, I could set x:Name values, but I'd rather not do that.
Any other ideas?
Update: Here is another example of such a problem:
<ListBox x:Name="topicList"
ItemsSource="{Binding Id, Converter={StaticResource topicGroupIDConverter}}"
SelectionChanged="ListBox_SelectionChanged"
HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
Width="150"
VerticalAlignment="Center"
ToolTipService.ToolTip="{Binding Description}"
ToolTipService.Placement="Right" />
<Button ToolTipService.ToolTip="Remove this topic from this group"
Visibility="{Binding ElementName=topicList,
Path=DataContext.Editable,
Converter={StaticResource visibilityConverter}}"
Click="removeTopicButton_Click"
HorizontalAlignment="Right"
Margin="10,0">
<Image Source="Images/appbar.cancel.rest.png" />
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When the button is clicked, I want to access topicList.DataContext. However, topicList itself is a DataTemplate in an ItemsControl, so I can't access it using its name from code-behind. How else can I do this?
You can add a property, say SelectedItemInAutoCompleteBox, to your presenter, and then can bind it to the SelectedItem property of AutoCompleteBox, using Mode=TwoWay, like this,
<sdk:AutoCompleteBox SelectedItem="{Binding Path=DataContext.SelectedItemInAutoCompleteBox, Mode=TwoWay}" ... />
You may try the same approach with Text property of AutoCompleteBox, also. See if it solves your problem.:-)
You have several choices:
If you're on Silverlight 5, use the AncestorBinding
Otherwise, use a Silverlight 4 AncestorBinding hack (it doesn't look pretty)
Or you could try DataContextProxy, which stores the DataContext in a resource so that it is accessible. Note: you should set the DataContextProxy as a Resource of topicList ListBox, not the UserControl as in Dan Wahlin's example.
Can you surround Datafields in a border? I have a large form that needs to be organized into sections, "Customer INformation" for instance.
Is there a way to surround these with a border?
I kinda get the feeling you are not specifying your own edit template for the control but a letting data form do it for you. I get this feeling because if you are already using an edit template you wouldn't be asking the question. Consider this:-
<dataform:DataForm x:Name="dataForm">
<dataform:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<Border BorderBrush="Black" BorderThickness="2">
<StackPanel>
<dataform:DataField>
<TextBox Text="{Binding ID, Mode=TwoWay}" />
</dataform:DataField>
<dataform:DataField>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
</dataform:DataField>
</StackPanel>
</Border>
<dataform:DataField>
<CheckBox IsChecked="{Binding Test, Mode=TwoWay}" />
</dataform:DataField>
</StackPanel>
</DataTemplate>
</dataform:DataForm.EditTemplate>
</dataform:DataForm>
Adding a border around the ID and Name fields is a simple case of placing them in their own StackPanel and putting that in a Border. Basically with a template you can do anything you want with the form appearance, in fact you don't even need the DataField if you feel you can do a better job with label placement etc in your own XAML.