Well...The first problem I had was setting the ItemsSource in XAML. I couldn't figure it out, so I do it in the form constrctor. Seeing more of your code, I think I see a slightly diffferent structure... As far as my ComboBox goes, it is not in a UserControl, it's just a plain UI control in a HeaderedContentControl (I wonder where I learned about that), that is also in a StackPanel, that is also in a Grid. I tried to set ItemsSource="{Binding Path=custs, ElementName=______ (don't know what to put here).
So, in code-behind, I set the DataContext of gridMain to a Job, and I also set the ItemsSource on the ComboBox to the full list of Customer objects (the "custs" collection). I really want to figure out setting the ItemsSource in XAML, but haven't so far.
<Grid x:Name="gridMain" Height="454" Width="591">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel x:Name="spHeader" Orientation="Horizontal" Width="Auto" >
<StackPanel x:Name="spCustomerData">
<HeaderedContentControl Header="Customer:" Width="150">
<ComboBox x:Name="dropdownCustomer" IsSynchronizedWithCurrentItem="True" AllowDrop="True"
DisplayMemberPath="company" SelectedValuePath="custno"
SelectedValue="{Binding Path=cust_num, Mode=TwoWay}"
SelectionChanged="dropdownCustomer_SelectionChanged"
/>
</HeaderedContentControl>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Ph: " Margin="10,0,0,0"/>
<TextBlock Text="{Binding Path=Customer.phone}" />
</StackPanel>
</StackPanel>
<..... />
</Grid>
Full code:
namespace wpf2
{
public partial class WindowJobEdit : Window
{
private string _JobNo;
DataClasses1DataContext ctx = new DataClasses1DataContext();
Job JobToEdit;
public IQueryable<Customer> custs;
public WindowJobEdit(string JobNo)
{
custs = from c in ctx.Customers
select c;
InitializeComponent();
dropdownCustomer.ItemsSource = custs; //Set the UI ComboBox list choices
GetJobToEdit(JobNo);
gridMain.DataContext = JobToEdit;
}
public void GetJobToEdit(string JobNo)
{
_JobNo = JobNo;
if (JobNo != null)
{
JobToEdit = ctx.Jobs.Single(o => o.job_num == JobNo);
}
}
private void dropdownCustomer_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox SendingObject = (ComboBox)sender;
if (JobToEdit != null)
{
JobToEdit.Customer = (Customer)SendingObject.SelectedItem;
}
}
However, that is not really the issue with the greater goal here of updating the Customer reference in a Job from the XAML binding, so I've just been letting the ItemsSource thing slide for now. But I'm certainly curious to figure it out.
Also, is your DataStores a collection of strings? I notice you are not having to use DisplayMemberValue and SelectedItemPath. My ItemsSource is a collection of Customer objects, so I have to specify which field on the Customer object is what part. I see that you are using SelectedValue but not the other two.
So you are able to point the ItemsSource to a Path on your UserControl, but what do I need to point to in order to get at the public custs collection?
>>Hope I'm not bothering you here
>
>Not at all.
>
>What you learned fits with what I've seen.
>
>My example was working code that I pulled from one of my forms I changed a few things to fit with your dojects. So... the question is why didn't it work?
>
>
<ComboBox ItemsSource="{Binding Path=Customers, ElementName=thisControl}"
> SelectedValue="{Binding Path=Customer, Mode=TwoWay}" >
> <ComboBox.ItemTemplate>
> <DataTemplate>
> <TextBlock Text="{Binding Path=company}"/>
> </DataTemplate>
> </ComboBox.ItemTemplate>
></ComboBox>
>
>From your example ItemsSource does appear to be a collection of Customer objects, so that looks right.
>
>It also looks like your DataContext is set to JobToEdit so that also looks right. Are you getting a binding error in the output window?
>
>Here's the actual code on my system... maybe it will help:
>
>C#
public partial class cdsJobInfoPage : UserControl
> {
> private cdsJobObject JobContext;
> public IEnumerable<cdsDataStore> DataStores { get; set; }
> public cdsJobInfoPage(cdsJobObject job)
> {
> JobContext = job;
> DataContext = job.Entity; // the entity here is my JobToEdit
> DataStores = job.GetDataStores();
> InitializeComponent();
> }
>
>XAML
<UserControl ... Name="ucJobInfo"> ...
> <ComboBox Grid.Row="3"
> Grid.Column="1"
> Name="cmbDataStores"
> Height="20"
> ItemsSource="{Binding Path=DataStores, ElementName=ucJobInfo}"
> SelectedValue="{Binding Path=cdsDataStore, Mode=TwoWay}" >
> <ComboBox.ItemTemplate>
> <DataTemplate>
> <TextBlock Text="{Binding Path=Name}"/>
> </DataTemplate>
> </ComboBox.ItemTemplate>
> </ComboBox>
>
>My jobs are data processing projects so this sets the root of the folder tree where the jobs data files will be stored.
>
>See if you find anything in that. And if you don't let me know exactly what it is doing and not doing.
>
>John
>
>>Hope I'm not bothering you here, but this is a MAJOR hurdle... I'm pulling my hair out over this one!! Spent HOURS today on trying to update the Job.cust_num via pure XAML binding, and I have not accomplished it yet (without using code-behind on SelectionChanged).
>>
>>Here's a few things I learned (could be wrong on some of this, so please feel free to correct me)
>>
>>With the Linq-to-Sql modelling and the Association between Job.cust_num and Customer.custno like we are hashing around with here, you CANNOT set the cust_num field of the Job object directly. You get this error in the output window: "A first chance exception of type 'System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException' occurred in wpf2.exe"
>>
>>And, naturally, the very XAML that I wrote for my UI is causing said error every time I make a selection in the ComboBox, as it is trying set Job.cust_num to the value of the selected item, as the binding instructions have told it to.
>>
>>
>> <ComboBox x:Name="dropdownCustomer" IsSynchronizedWithCurrentItem="True" AllowDrop="True"
>> DisplayMemberPath="company" SelectedValuePath="custno"
>> SelectedValue="{Binding Path=cust_num, Mode=TwoWay}"
>> />
>>
>>
>>
>>So, we have already learned that you must change the Customer object within the Job to accomplish what we are after (as far as I have learned from more than one place, this is the way it must be done).
>>
>>Therefore, I did get it working with the SelectionChanged method and some simple code-behind:
>>
>>
>> <ComboBox x:Name="dropdownCustomer" IsSynchronizedWithCurrentItem="True" AllowDrop="True"
>> DisplayMemberPath="company" SelectedValuePath="custno"
>> SelectedValue="{Binding Path=cust_num, Mode=TwoWay}"
>> SelectionChanged="dropdownCustomer_SelectionChanged"
>> />
>>
>>
>>Code-behind:
>>
>> private void dropdownCustomer_SelectionChanged(object sender, SelectionChangedEventArgs e)
>> {
>> ComboBox SendingObject = (ComboBox)sender;
>>
>> if (JobToEdit != null) // must check... is null at form launch
>> JobToEdit.Customer = (Customer)SendingObject.SelectedItem;
>> }
>> }
>>
>>
>>
>>By the way, even though the Customer object is from a totally different source context, this does work! (I had heard that objects that are EXACTLY the same are handled this way by the CLR.)
>>
>>Only thing I still don't like is: Because of the SelectedItem="{Binding Path=cust_num}" in the XAML (which is necessary to point the ComboBox to the correct Customer at the launching of the form), although you do not get a run-time error, and everything in the app works exactly like I want, BUT, if you look at the Output Window it is giving that stupid "A first chance exception of type 'System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException' occurred in wpf2.exe" error with every selection of the ComboBox. I need to figure out how to synch up without SelectedValue.
>>
>>I have not found one single example on the internet that addresses this matter thoroughly enough to show what to do. Most examples are based on ad-hoc made-on-the-fly object lists that work good for demo, and they sure make it look easy. Let's see 'em do it against real data from a Linq-to-Sql model.
>>
>>
>>
>>I did experiment with your suggestion on trying to bind SelectedValue to a Customer reference, but I could not get it wired the right way to make it work.
>>
>>
>>
>>
>>
>>>>Dude, I hope you read this soon!!
>>>
>>>I did. <g>
>>>
>>>Your catching up to me I learned about this a few weeks ago. What confuses me is I have changing the ID working fine in one of my classes and I don't know why it is working. <g>
>>>
>>>What you found was the next thing to try, but let me propose a slightly different solution.
>>>
>>>
>>><ComboBox ItemsSource="{Binding Path=Customers, ElementName=thisControl, Mode=TwoWay}"
>>> SelectedValue="{Binding Path=Customer, Mode=TwoWay}" >
>>> <ComboBox.ItemTemplate>
>>> <DataTemplate>
>>> <TextBlock Text="{Binding Path=company}"/>
>>> </DataTemplate>
>>> </ComboBox.ItemTemplate>
>>></ComboBox>
>>>
>>>
>>>Customers is a list of Customer objects.
>>>Customer is Job.Customer (The object not the ID)
>>>company is Customer.company
>>>
>>>Since the selected value is the object not the id you should not need the extension to your class.
>>>
>>>The one point of uncertainty I have is...
>>>
>>>If the Customers list is from a different data context than the Job object... does it work?
>>>
>>>(This was a long post and I tried to answer it quickly so hopefully I understood it all correctly.)
>>>
>>>John
>>>
>>>