How to override parent datacontext in child panel - wpf

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.

Related

Removing duplicated XAML code by using custom control?

What is a good way to get rid of repeating XAML in different files. Example :
<StackPanel Grid.Row="8" Grid.Column="2" Style="{StaticResource ViewContentStyle}" Visibility="{Binding Type, Converter={StaticResource TypeToVisibility}}">
<ctl:NewLabel LabelContent="{x:Static common:LocalResources.UNameLabel}" LabelStyle="{DynamicResource ContentLabelStyle}"
ImageStyle="{DynamicResource ViewContentControlStyle}">
<ctl:ETextBox x:Name="UserName" HorizontalAlignment="Left" Style="{StaticResource {x:Type TextBox}}"
LostFocus="Textbox_OnLostFocus"
Text="{Binding Path=UserName, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True, ValidatesOnDataErrors=True}">
</ctl:ETextBox>
</ctl:NewLabel>
</StackPanel>
<StackPanel Grid.Row="9" Grid.Column="2" Style="{StaticResource ViewContentStyle}" Visibility="{Binding SelectedAuthenticationType, Converter={StaticResource AuthToVisibility}}">
<StackPanel Orientation="Horizontal" KeyboardNavigation.TabNavigation="None">
<Label Style="{DynamicResource ContentLabelStyle}" Content="{x:Static common:LocalResources.UPasswordLabel}"/>
<AdornerDecorator>
<PwdBox x:Name="Password"
HorizontalAlignment="Left"
LostFocus="Textbox_OnLostFocus" PasswordChar="*"
</PwdBox>
</AdornerDecorator>
</StackPanel>
I have 3 files where almost the same code is reused. I think there is a way to get rid of this by using a common custom control. However, I dont see much examples as to how it can be done. Any leads would be great.
Add a new UserControl to your project and move the common XAML to this one.
You could then create an instance of this UserControl (replace "UserControl1" with the actual name of your UserControl) in any other view:
<!--insert the shared markup here: -->
<local:UserControl1 />

Adding custom values to ComboBox with CompositeCollection

I'm new to WPF. I have a combobox which when choosing a value three other fields (AbbrBlock, MultiBrandSupplier, IgnoreNoCompetition) update along to show the correct relevant values according to the data source. No problem with this.
Issue arises when I try to add to the combobox a custom value, although the combobox shows all values correctly, the other fields don't change when changing the combobox's value.
Here's the working code (without the additional custom combobox value - stripped to the key pieces):
<Window.Resources>
<local:OrdersDataSet x:Key="ordersDataSet" />
<CollectionViewSource x:Key="caSuppliersViewSource" Source="{Binding CaSuppliers, Source={StaticResource ordersDataSet}}"/>
</Window.Resources>
...
<StackPanel DataContext="{StaticResource caSuppliersViewSource}">
<ComboBox Name="SupplierDropdown" DisplayMemberPath="SupplierName"
ItemsSource="{Binding Source={StaticResource ResourceKey=caSuppliersViewSource}}"/>
<TextBlock Name="AbbrBlock" VerticalAlignment="Center" Text="{Binding Abbr}"/>
<CheckBox Name="MultiBrandSupplier" IsChecked="{Binding MultiBrand}"/>
<CheckBox Name="IgnoreNoCompetition" IsChecked="{Binding IgnoreNoCompetition}"/>
</StackPanel>
Here's the code with the added custom value which shows correctly but the other fields don't update when changing the combobox value:
<Window.Resources>
<local:OrdersDataSet x:Key="ordersDataSet" />
<CollectionViewSource x:Key="caSuppliersViewSource" Source="{Binding CaSuppliers, Source={StaticResource ordersDataSet}}"/>
</Window.Resources>
...
<StackPanel DataContext="{StaticResource caSuppliersViewSource}">
<StackPanel.Resources>
<CompositeCollection x:Key="myCompositeCollection">
<CollectionContainer Collection="{Binding Source={StaticResource ResourceKey=caSuppliersViewSource}}" />
<ComboBoxItem Content="Add New..." />
</CompositeCollection>
</StackPanel.Resources>
<ComboBox Name="SupplierDropdown" DisplayMemberPath="SupplierName"
ItemsSource="{Binding Source={StaticResource myCompositeCollection}}"/>
<TextBlock Name="AbbrBlock" VerticalAlignment="Center" Text="{Binding Abbr}"/>
<CheckBox Name="MultiBrandSupplier" IsChecked="{Binding MultiBrand}"/>
<CheckBox Name="IgnoreNoCompetition" IsChecked="{Binding IgnoreNoCompetition}"/>
</StackPanel>
What am I missing here?
Looks like the ComboBox was updating caSuppliersViewSource's View.CurrentItem property (I think) to match its SelectedItem in your first snippet. In the second, the CollectionViewSource is buried inside a CompositeCollection so that doesn't happen any more. However, the ComboBox is still selecting an item, and you can just bind to that using ElementName. No need for setting the DataContext on the StackPanel with this version.
<StackPanel>
<StackPanel.Resources>
<CompositeCollection x:Key="myCompositeCollection">
<CollectionContainer Collection="{Binding Source={StaticResource ResourceKey=caSuppliersViewSource}}" />
<ComboBoxItem Content="Add New..." />
</CompositeCollection>
</StackPanel.Resources>
<ComboBox
Name="SupplierDropdown"
DisplayMemberPath="SupplierName"
ItemsSource="{Binding Source={StaticResource myCompositeCollection}}"
/>
<TextBlock
Name="AbbrBlock"
VerticalAlignment="Center"
Text="{Binding SelectedItem.Abbr, ElementName=SupplierDropdown}"
/>
<CheckBox
Name="MultiBrandSupplier"
IsChecked="{Binding SelectedItem.MultiBrand, ElementName=SupplierDropdown}"
/>
<CheckBox
Name="IgnoreNoCompetition"
IsChecked="{Binding SelectedItem.IgnoreNoCompetition, ElementName=SupplierDropdown}"
/>
</StackPanel>
You could also give eyour viewmodel a SelectedDBItem property of the same type as whatever caSuppliersViewSource contains, and bind ComboBox.SelectedItem to that. Then you could do this:
<TextBlock
Name="AbbrBlock"
VerticalAlignment="Center"
Text="{Binding SelectedDBItem}"
/>
But that's six dozen of one, half of another, or something -- unless you want to do something else with SelectedDBItem in your viewmodel, then it's handy.

WPF Combobox based databinding Textbox

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;

bind textblock to current listbox item in pure xaml

i have some data saved in a xml file.
Those will be displayed in a listbox.
Now, when i change the listbox Selectedindex, i would like to update the other information in the textblock based on the selectedindex.
is there any way to do this in pure xaml?
if not, how would i bind the Textblock to the selecteditem in the listbox ?
EDIT:
How would i navigate through the Data without using the listbox? i mean using a button to move to next item and other button to move backward..!!
any help is really appreciated..
<Window x:Class="WpfSingleInstance.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>
<StackPanel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Cornsilk">
<StackPanel.Resources>
<XmlDataProvider x:Key="InventoryData" XPath="Inventory/Books">
<x:XData>
<Inventory xmlns="">
<Books>
<Book ISBN="0-7356-0562-9" Stock="in" Number="9">
<Title>XML in Action</Title>
<Summary>XML Web Technology</Summary>
</Book>
<Book ISBN="0-7356-1370-2" Stock="in" Number="8">
<Title>Programming Microsoft Windows With C#</Title>
<Summary>C# Programming using the .NET Framework</Summary>
</Book>
<Book ISBN="0-7356-1288-9" Stock="out" Number="7">
<Title>Inside C#</Title>
<Summary>C# Language Programming</Summary>
</Book>
</Books>
</Inventory>
</x:XData>
</XmlDataProvider>
</StackPanel.Resources>
<TextBlock FontSize="18" FontWeight="Bold" Margin="10"
HorizontalAlignment="Center">XML Data Source Sample</TextBlock>
<ListBox
Width="265" Height="98" x:Name="lbox" Background="Honeydew" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemsSource>
<Binding Source="{StaticResource InventoryData}"
XPath="*[#Stock='out'] | *[#Number>=8 or #Number=3]"/>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="12" Foreground="Red">
<TextBlock.Text>
<Binding XPath="Title"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel DataContext="{StaticResource InventoryData}">
<TextBlock Text="{Binding XPath=Book/Title}"/>
<TextBox Margin="5,31,98,10" x:Name="textBoxMainDetail" Text="{Binding XPath=Book/Summary}" />
</StackPanel>
</StackPanel>
</Grid>
You could bind like this:
<TextBox Text="{Binding ElementName=lbox, Path=SelectedItem[Title].InnerText}" />
SelectedItem is an XmlElement.
EDIT: Here is a bit of sample code how to access the data of the XmlDataProvider in code behind and apply it as DataContent of a TextBox.
Change the TextBox.Text binding like this:
<TextBox x:Name="textBoxMainDetail" Text="{Binding Path=[Title].InnerText}" />
In code behind get the XML data from the XmlDataProvider and set the DataContext of the TextBox:
XmlDataProvider dataProvider = (XmlDataProvider)stackPanel.Resources["InventoryData"];
XmlElement books = (XmlElement)dataProvider.Document.SelectNodes(dataProvider.XPath)[0];
// set DataContext to an item from the child node collection
textBoxMainDetail.DataContext = books.ChildNodes[0];
Note that the StackPanel with the XmlDataProvider in its resource dictionary has now got a name. If this code shall run during application initialization (e.g. in Window constructor), the XmlDataProvider.IsAsynchronous property must be set to false.
You should now be able to change the DataContext to another indexed item of the books collection in a button click handler.
You need to set the SelectedValuePath as the 'Title' for your listbox. Then simply bind the your textBlock to the selectedValue of your listbox using elementName like this -
<ListBox Width="265" Height="98" x:Name="lbox" Background="Honeydew"
IsSynchronizedWithCurrentItem="True" SelectedValuePath="Title">
</ListBox>
<StackPanel DataContext="{StaticResource InventoryData}">
<TextBlock Text="{Binding Path=SelectedValue, ElementName=lbox}"/>
<TextBox Margin="5,31,98,10" x:Name="textBoxMainDetail" Text="{Binding XPath=Book/Summary}" />
</StackPanel>
</StackPanel>

Binding a wpf listbox to a combobox

I have created a very basic wpf application that I want to use to record time entries against different projects.
I havent used mvvm for this as I think its an overkill.
I have a form that contains a combobox and a listbox. I have created a basic entity model like this
What I am trying to do is bind the combobox to Project and whenever I select an item from the combobox it updates the listview with the available tasks associated with that project.
This is my xaml so far. I dont have any code behind as I have simply clicked on that Data menu and then datasources and dragged and dropped the items over. The application loads ok and the combobox is been populated however nothing is displaying in the listbox.
Can anyone tell me what I have missed?
<Window.Resources>
<CollectionViewSource x:Key="tasksViewSource" d:DesignSource="{d:DesignInstance l:Task, CreateList=True}" />
<CollectionViewSource x:Key="projectsViewSource" d:DesignSource="{d:DesignInstance l:Project, CreateList=True}" />
</Window.Resources>
<Grid DataContext="{StaticResource tasksViewSource}">
<l:NotificationAreaIcon
Text="Time Management"
Icon="Resources\NotificationAreaIcon.ico"
MouseDoubleClick="OnNotificationAreaIconDoubleClick">
<l:NotificationAreaIcon.MenuItems>
<forms:MenuItem Text="Open" Click="OnMenuItemOpenClick" DefaultItem="True" />
<forms:MenuItem Text="-" />
<forms:MenuItem Text="Exit" Click="OnMenuItemExitClick" />
</l:NotificationAreaIcon.MenuItems>
</l:NotificationAreaIcon>
<Button Content="Insert" Height="23" HorizontalAlignment="Left" Margin="150,223,0,0" Name="btnInsert" VerticalAlignment="Top" Width="46" Click="btnInsert_Click" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="70,16,0,0" Name="comProjects" VerticalAlignment="Top" Width="177" DisplayMemberPath="Project1" ItemsSource="{Binding Source={StaticResource projectsViewSource}}" SelectedValuePath="ProjectID" />
<Label Content="Projects" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" IsEnabled="False" />
<Label Content="Tasks" Height="28" HorizontalAlignment="Left" Margin="16,61,0,0" Name="label2" VerticalAlignment="Top" />
<ListBox Height="112" HorizontalAlignment="Left" Margin="16,87,0,0" Name="lstTasks" VerticalAlignment="Top" Width="231" DisplayMemberPath="Task1" ItemsSource="{Binding Path=ProjectID, Source=comProjects}" SelectedValuePath="TaskID" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="101,224,0,0" Name="txtMinutes" VerticalAlignment="Top" Width="42" />
<Label Content="Mins to Insert" Height="28" HorizontalAlignment="Left" Margin="12,224,0,0" Name="label3" VerticalAlignment="Top" />
<Button Content="None" Height="23" HorizontalAlignment="Left" Margin="203,223,0,0" Name="btnNone" VerticalAlignment="Top" Width="44" />
</Grid>
You set the DataContext in the Grid, so just change your ItemsSource as indicated below.
<Grid DataContext="{StaticResource tasksViewSource}">
<ListBox Height="112"
HorizontalAlignment="Left"
Margin="16,87,0,0"
Name="lstTasks"
VerticalAlignment="Top"
Width="231"
DisplayMemberPath="Task1"
ItemsSource="{Binding}"
SelectedValuePath="TaskID" />
</Grid>
You'll also want to filter the task list, to do that just change the generated code.
Here is an example I hacked together.
You can toggle the value using the SelectionChanged event from your ComboBox.
private System.Data.Objects.ObjectQuery<Models.Task> GetTasksQuery(Models.StackoverflowEntities stackoverflowEntities)
{
// get the selected item
int id = (int)cboProjects.SelectedValue;
System.Data.Objects.ObjectQuery<CollectionViewSourceEF.Models.Task> tasksQuery = stackoverflowEntities.Tasks.Where(task => task.ProjectID == id) as ObjectQuery<Task>;
return tasksQuery;
}

Resources