How can I nest multiple groupbox's inside a tab control tab? - c#

I created a MVVM Stock app fashioned after Josh Smiths MVVM demo. Inside a tab on the right pane I put a stock option chain consisting of two datagrids - one for calls and one for puts.
These represent all the options for a single expiration month.
However, I want to show multiple months inside the same tab - side by side (I was thinking of putting each month's data inside a groupbox) with the ability to see several months data at a time.
In accordance with Josh's demo, each tab contains data presented by a viewmodel and is added to the tab control as a workspace.
I believe I can nest multiple MonthlyChainSpaces inside a single WorkSpace tab, but I'm not sure how the xaml should look. I have a usercontrol that is tied to the viewmodel, so I don't think that would require much change, if any, as the data in each GroupBox would be presented by my current viewmodel's, but instead of each appearing on a separate tab, they'd be nested inside a single tab.
I'm not sure about which controls I need to put in my "MonthlyChainSpaces" DataTemplate in order to accomplish this. I know that each groupbox (and I'll have a need for a variable number of them inside each tab) can only have one item - so I was thinking of a stackpanel or grid inside the groupbox. But I'm not sure how to accomplish what I want. Do I need a "master groupbox" (which sits inside the tab) and within that groupbox, I have nested groupbox's, each representing a groupbox item of the "master groupbox"?
Since I'm fairly new with WPF I'd appreciate it if someone with more experience could provide a little direction on how to make this happen?

Given the following xaml...
<Window x:Class="NestedGroupBoxes.MainWindow"
xmlns:datagrid="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="525" Width="767">
<Window.Resources>
<XmlDataProvider x:Key="chainProvider" Source="/MinimalOptions.XML" XPath="/query/results">
</XmlDataProvider>
</Window.Resources>
<DockPanel>
<GroupBox MaxWidth="500" HorizontalAlignment="Left">
<GroupBox.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding Source={StaticResource chainProvider}, XPath=optionsChain/#symbol}"/>
</StackPanel>
</DataTemplate>
</GroupBox.HeaderTemplate>
<Grid DockPanel.Dock="Bottom" Margin="4">
<StackPanel Orientation="Vertical">
<Label Content="AllOptions" HorizontalAlignment="Left" Name="outerOptionChainDataGrid" VerticalAlignment="Top" />
<datagrid:DataGrid MinHeight="200"
MinWidth="200"
MaxWidth="500"
Width="Auto"
HorizontalAlignment="Left"
AutoGenerateColumns="False"
EnableRowVirtualization="True"
AlternatingRowBackground="LightGray"
SelectionUnit="FullRow"
Name="dgridCallOptionChain"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource chainProvider},XPath=optionsChain/option}"
SelectedItem="{Binding Path=SelectedOption, Mode=TwoWay}"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" IsManipulationEnabled="True" >
<datagrid:DataGrid.Columns>
<datagrid:DataGridTemplateColumn Header="Date" MinWidth="100">
<datagrid:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<GroupBox MaxWidth="500" HorizontalAlignment="Left">
<GroupBox.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding Source={StaticResource chainProvider}, XPath=optionsChain/#symbol}"/>
</StackPanel>
</DataTemplate>
</GroupBox.HeaderTemplate>
<Grid DockPanel.Dock="Bottom" Margin="0,2,4,2">
<StackPanel Orientation="Vertical">
<Label Content="Monthly Options" HorizontalAlignment="Left" Name="innerOptionChainDataGrid" VerticalAlignment="Top" />
<datagrid:DataGrid MinHeight="200"
MinWidth="200"
MaxWidth="500"
Width="Auto"
HorizontalAlignment="Left"
AutoGenerateColumns="False"
EnableRowVirtualization="True"
AlternatingRowBackground="LightGray"
SelectionUnit="FullRow"
Name="dgridCallOptionChain"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource chainProvider},XPath=optionsChain/option}"
SelectedItem="{Binding Path=SelectedOption, Mode=TwoWay}"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" MaxHeight="200" IsManipulationEnabled="True" >
<datagrid:DataGrid.Columns>
<datagrid:DataGridTextColumn Header="Symbol" Binding="{Binding XPath=#symbol}"/>
<datagrid:DataGridTextColumn Header="Strike" Binding="{Binding XPath=strikePrice}"/>
<datagrid:DataGridTextColumn Header="Bid" Binding="{Binding XPath=bid}"/>
<datagrid:DataGridTextColumn Header="Ask" Binding="{Binding XPath=ask}"/>
<datagrid:DataGridTextColumn Header="Volume" Binding="{Binding XPath=vol}"/>
<datagrid:DataGridTextColumn Header="OpenInt" Binding="{Binding XPath=openInt}"/>
</datagrid:DataGrid.Columns>
</datagrid:DataGrid>
</StackPanel>
</Grid>
</GroupBox>
</DockPanel>
</DataTemplate>
</datagrid:DataGridTemplateColumn.CellTemplate>
</datagrid:DataGridTemplateColumn>
</datagrid:DataGrid.Columns>
</datagrid:DataGrid>
</StackPanel>
</Grid>
</GroupBox>
</DockPanel>
</Window>
And the following xml data file:
<?xml version="1.0" encoding="UTF-8"?>
<query xmlns:yahoo="http://www.yahooapis.com/v1/base.rng"
yahoo:count="1" yahoo:created="2012-01-10T00:51:10Z" yahoo:lang="en-US">
<diagnostics>
<publiclyCallable>true</publiclyCallable>
<url execution-start-time="21" execution-stop-time="286"
execution-time="265" proxy="DEFAULT"><![CDATA[http://www.datatables.org/yahoo/finance/yahoo.finance.options.xml]]></url>
<url execution-start-time="291" execution-stop-time="1179"
execution-time="888" proxy="DEFAULT"><![CDATA[http://finance.yahoo.com/q/op?s=YHOO&m=2012-01]]></url>
<log>results.length(): 2</log>
<javascript execution-time="987" instructions-used="406004" table-name="yahoo.finance.options"/>
<user-time>1276</user-time>
<service-time>1153</service-time>
<build-version>24402</build-version>
</diagnostics>
<results>
<optionsChain expiration="2012-01-20" symbol="YHOO">
<option symbol="YHOO120121C00002500" type="C">
<strikePrice>2.5</strikePrice>
<lastPrice>13.65</lastPrice>
<change>0</change>
<changeDir/>
<bid>12.9</bid>
<ask>13</ask>
<vol>8</vol>
<openInt>73</openInt>
</option>
<option symbol="YHOO120121C00005000" type="C">
<strikePrice>5</strikePrice>
<lastPrice>11.30</lastPrice>
<change>0</change>
<changeDir/>
<bid>10.4</bid>
<ask>10.5</ask>
<vol>NaN</vol>
<openInt>289</openInt>
</option>
<option symbol="YHOO120121C00007500" type="C">
<strikePrice>7.5</strikePrice>
<lastPrice>8.70</lastPrice>
<change>0</change>
<changeDir/>
<bid>7.9</bid>
<ask>8</ask>
<vol>5</vol>
<openInt>1416</openInt>
</option>
</optionsChain>
</results>
Right now it will display multiple groupbox's with datagrids inside. However, there is a separate groupbox/datagrid for each corresponding "option" node in the xml file. What I want given the xml file is a single groupbox with a datagrid inside enclosed by another datagrid. My approach is to have the outer datagrid bind to a collection of viewmodel's each of which will produce the inner groupbox/datagrid. In other words the inner groupbox/datagrid's will be stacked horizontally - each in a column of the outer datagrid. The outer datagrid will have a single row with multiple columns depending on the number of viewmodel's in the outer datagrid's bound collection.
So, say the outer datagrid is bound to a collection like:
ObservableCollection allOptions = new
ObservableCollection();
This collection will populate the outer datagrid with objects which are viewmodel's of type OptionChainViewModel. These "inner" objects are themselves GroupBox/DataGrid views each containing the options for a single month. The result will be an outer groupbox with a single row datagrid with multiple columns. Each outer DataGrid column will in turn contain a GroupBox/DataGrid view as rendered by the OptionChainViewModel.
Hope this helps trigger some replies...

Related

How to bind properties of one ObservableCollection of ListView to properties of SelectedItem of another ListView?

So I have a few ListViews. The first is binded to ObservaleCollection<ComPort>. All properties of ComPort may take some predefined values. Other ListViews are responsible for that properties: they show all that possible (predefined) values and SelectedItem should be the current value of that property of ComPort from the first ObservaleCollection.
I can't attach images so here is an external picture, it would make the situation clean: http://i.stack.imgur.com/ZBRRx.png
<Window.Resources>
<ResourceDictionary x:Name="rd">
<l:ComPorts x:Key="vComPorts"/>
<l:SystemPorts x:Key="vSystemPorts"/>
<l:BaudRates x:Key="vBaudRate"/>
<l:Parities x:Key="vParities"/>
<l:DataBits x:Key="vDataBits"/>
<l:StopBits x:Key="vStopBits"/>
<l:Timeouts x:Key="vTimeouts"/>
<l:ComPort x:Key="vSelectedPort"/>
</ResourceDictionary>
</Window.Resources>
...
<ListView
Name="PortsList"
Grid.Row="1"
Grid.Column="0"
Margin="5"
VerticalAlignment="Stretch"
ItemsSource="{StaticResource vComPorts}"
DataContext="{StaticResource vComPorts}"
SelectedValuePath="PortName"
SelectedValue="{Binding ElementName=SystemPortsList, Path=SelectedItem.Value}"
SelectionChanged="PortsList_SelectionChanged"
MouseDoubleClick="PortsList_MouseDoubleClick">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox />
<TextBlock Margin="5,0,0,0" Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView
x:Name="SystemPortsList"
Margin="5"
VerticalAlignment="Stretch"
DataContext="{Binding Source={StaticResource vSelectedPort}}"
ItemsSource="{Binding Source={StaticResource vSystemPortsView}}"
SelectedItem="{Binding Source={StaticResource vSelectedPort}, Path=PortName}"
MouseEnter="SystemPortsList_Refresh"
MouseLeave="SystemPortsList_Refresh"
Grid.Row="1"
Grid.Column="1" SelectionChanged="SystemPortsList_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="tb" Margin="5,0,0,0" Text="{Binding Path=Name}" />
</StackPanel>
</ListView.ItemTemplate>
</ListView>
I've tried to make an instance of class ComPort for saving current value of selected item from the first ListView, but anyway I can't cope with it without help. How this task should be solved?
1) Instead of handling SelectionChanged on the PortsList ListView, bind your checkbox to the ListViewItemsPanel like so:
<CheckBox IsChecked={Binding IsSelected, RelativeSource=Parent/>
2) Add an x:Name to your first ListBox, say x:Name="ComPortLB";
3) Remove DataContext on SystemPortsList;
4) Fix SelectedItem on SystemPortsList like so:
SelectedValue="{Binding ElementName=ComPortLB, Path=SelectedValue.PortName}"
I haven't tested any of this code and I haven't done this kind of stuff for a while, so I apologize for errors, but it should get you closer. I've also had to make some assumptions about your classes since you don't provide enough information.

Treeview Instead of listbox in AutoCompleteBox Silverlight

I need an AutoCompleteBox that this will show results in a TreeView according to their parents.
This operation does not need to be dynamic. The only requirement is, that the user should be able to see AutoCompleteBox pop-up results with their parents like a tree.
Is there any way to implement this kind of component or any ready component for this?
To have a Treeview Inside the AutoCompleteBox,
<sdk:AutoCompleteBox IsTextCompletionEnabled="True" AllowDrop="True" ItemsSource="{Binding}" x:Name="txtMain" Height="25">
<sdk:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<sdk:TreeView ItemsSource="{Binding YourCollection}" Name="treeView1">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding YourCollection}">
<TextBlock Text="{Binding Property}" />
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
</DataTemplate>
</sdk:AutoCompleteBox.ItemTemplate>
</sdk:AutoCompleteBox>

WPF - Dynamic content in dynamics TabControls

Even the title makes me laugh...
So, my code looks like that so far :
<Grid>
<WrapPanel x:Name="wpVTList" Margin="70,356,1541,150">
<TabControl ItemsSource="{Binding VTList}"
TabStripPlacement="Left"
Padding="0,10,0,0"
x:Name="tcVTList">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
Width="270"
FontSize="20"
FontFamily="Calibri"
Margin="0,5,0,5"
Padding="30,0,0,0"
Height="27"
VerticalAlignment="Center"
HorizontalAlignment="Left" MouseUp="tbkVTList_MouseUp" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</WrapPanel>
Here is my MainWindow, or at least the window which will display everything, where I create as much tabs as I have in an ObservableCollection of string, which is supposed to be filled dynamically by a query or something.
Beside, I've created two customs userControls like that :
<UserControl x:Class="Configurateur.Configuration.TableauEcrans"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d"
d:DesignHeight="175" Width="470">
<Grid>
<Grid.Resources>
...
</Grid.Resources>
<telerik:RadGridView ItemsSource="{Binding CamerasList}"
AutoGenerateColumns="False"
ClipToBounds="True"
IsSynchronizedWithCurrentItem="True"
telerik:StyleManager.Theme="Windows8"
CanUserFreezeColumns="False">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name}"
Header="Name"
UniqueName="Name"
Width="*"
HeaderTextAlignment="Center" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding Price}"
Header="Price"
UniqueName="Price"
Width="65"
HeaderTextAlignment="Center" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding Amount}"
Header="Amount"
UniqueName="Amount"
Width="100"
HeaderTextAlignment="Center" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</Grid>
</UserControl>
And then, back to my MainWindow I can use them by adding :
<Grid x:Name="gridTableaux" Margin="376,356,0,0">
<custom:UCCameras Margin="10,41,944,453" />
<custom:UCOwner Margin="10,411,1054,0" />
</Grid>
right after my wrapPanel. It works, they're all here. I put them in a grid in order to delimit where is the dynamic area in my MainWindow. Each userControl is a gridView, and a gridView is filled by an ObservableCollection of a class (i.e. Camera.cs) which contains properties like Name, bounded to the columns in my UC.
What I want to do is to display a new userControl in my MainWindow for each tab, with different informations. For example, if a user fills a row in the gridView in the first tab, he has to find an empty gridView if he chooses another tab. And then, if he comes back to the first tab, he is supposed to see what he wrote in the gridView at the beginning.
1) I don't really see how to use my UCs in each tab since they are created dynamically. I don't know where to put them in my code since I want them to be in the grid (the one which delimits the area where everything happens), at the other side of the MainWindow, or kind of.
2) I have absolutly no idea of how to save these entered values between each tab, and how to choose which one I can use (because of the dynamic way).
I suppooosed... that each tab is a list (ObservableCollection works fine for everything I do) of an OC of class. So my class is a camera (= a row of my gridView) with properties (= cells in my row), contained in an ObservableCollection (= what my gridView has), contained in an ObservableCollection> (= my tabs, with the main grid inside).
Any idea ? I hope I did something that makes sense for what I try to do, I'm still new in WPF. Maybe an OC of UC..?
Thanks for your help !

Is there a simple way to put a datagrid into a combobox?

Is there a simple example of how to have a combobox show a datagrid instead of a list? I been trying SO and google but kept getting results that show how to put a combobox in a datagrid.
To answer your first question, "Is there a simple example of how to have a combobox show a datagrid instead of a list?" very straight: - the answer is no.
That said, there are a couple of different ways to accomplish something that looks like a DataGrid (ie list with columns).
A very simple sample with faking something that could look like a datagrid could be altering the combobox's ItemTemplate
<ComboBox x:Name="cb" ItemsSource="{Binding Persons}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Text="{Binding Age}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You can also use SharedSizeGroup (and Grid.IsSharedSizeScope) to synchronize the column widths accross all items resulting in a dynamic and aligned layout.
It's for sure possible. That is a power of WPF.
This link explains how to do it.
As I said, it's weird to me to use a ComboBox for this. Maybe you can do it like this:
<ToggleButton x:Name="Toggle" Width="200" Height="30" HorizontalAlignment="Center" VerticalAlignment="Top" />
<Popup IsOpen="{Binding ElementName=Toggle, Path=IsChecked}" PlacementTarget="{Binding ElementName=Toggle}" Placement="Bottom" Width="{Binding ElementName=Toggle, Path=Width}">
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Test1" />
<DataGridTextColumn Header="Test2" />
</DataGrid.Columns>
</DataGrid>
</Popup>
It's only a example of course, and it's missing a few features like automatic Popup hiding. But you get the point.

WPF Listbox : How to bind data?

I have a listBox and add data to it like so:
for (int x = 0; x < Orchestrator.Instance.getTopicCount(); x++)
{
listTopics.Items.Add(Orchestrator.Instance.getTopic(x));
}
But I need to be able to do things like have text wrapping and divider lines, so I would like to do it the XAML.
Microsoft shows this:
<TextBlock Width="248" Height="24"
Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
Mode=OneWay}"
x:Name="tbSelectedColor"
Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
Mode=OneWay}"/>
But I don't really understand it. Here is my XAML:
<ListBox Height="261" HorizontalAlignment="Left" Margin="352,38,0,0" Name="listContent" VerticalAlignment="Top" Width="391" Grid.Column="1" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" MaxWidth="391" ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
How am I able to achieve what I want? (Divider lines, text wrapping so I don't have to scroll horizontally, and data binding)
To specify the layout of each item, there are two different things you can change: the ItemContainerStyle, which provides the ControlTemplate used for each ListBoxItem, or the ItemTemplate, which provides the DataTemplate that is used to render each data item. The ItemTemplate is simpler to use if you're just getting started and haven't gotten the hang of ControlTemplates yet.
To get your text to wrap, there are two key things to do. Turn off the default horizontal scrolling of ListBox (which you got already), and set the TextWrapping property of your TextBlock to Wrap. To get to the TextBlock you need to define it in your ItemTemplate. Here's a simple example of the template declared inline, though you could also pull it out as a Resource. For the dividing line I used the simplest approach of a Border with only a bottom black line.
<ListBox x:Name="listContent" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="Black">
<TextBlock Text="{Binding}" TextWrapping="Wrap"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It seems that you will have to ascend the rather steep learning curve for how to use databinding in WPF first. Thereafter you should learn about DataTemplates and ItemTemplates to get the dividers and so forth.
Try this to get you started
A book I can heartily recommend is WPF in Action from Manning.
You need to define the ItemTemplate of your ListBox.
In your resources, add this:
<DataTemplate x:Key="myItemTemplate" TargetType="ListBoxItem">
<TextBlock Text="{Binding}"/>
</DataTemplate>
Supposing that your getTopic method returns a string, otherwise use {Binding MyTopicProperty} where MyTopicProperty is a property in your Topic class. Customize the TextBlock as you need.
Then use your ListBox like this:
<ListBox ItemTemplate="{StaticResource myItemTemplate"/>
here is an example how to bind listbox with RSS feed with DataTemplate:
<UserControl.Resources>
<XmlDataProvider x:Key ="DataRSS" XPath="//item" Source="http://rss.feedsportal.com/c/629/f/502199/index.rss">< /XmlDataProvider>
</UserControl.Resources>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ListBox ItemsSource="{Binding Source={StaticResource DataRSS}}" Height="516" Margin="0,0,32,0" Background="{x:Null}" BorderBrush="#FF627DAE">
<ListBox.ItemTemplate >
<DataTemplate >
<Grid Width="400" Height="100" >
<Image Source="{Binding XPath=enclosure/#url}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBlock TextWrapping="Wrap" Text="{Binding XPath=title}" FontWeight="Bold" Grid.Column="2"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</grid>

Categories