WPF - Dynamic content in dynamics TabControls - c#

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 !

Related

How to bind controls to different source from a datatemplate

I have a simple design with several hubsections into which I want to display a ListBox inside a Grid. The ListBox itself will contain two elements : a TextBlock and a TextBox.
The TextBlock in each HubSection will display the same property, so the data binding is quite easy. However, the TextBox should display a different property in each separate HubSection. I am lost and do not even have a clue on how I should proceed.
Here is my XAML code :
<Page.Resources>
<ResourceDictionary>
<DataTemplate x:Key="BaseGridTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="12"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding PlayerName}"
FontSize="28"/>
<TextBox Text="{Binding OtherProperty, Mode=TwoWay}"
Background="Lavender" FontSize="28"
Grid.Column="2">
</TextBox>
</Grid>
</DataTemplate>
</ResourceDictionary>
</Page.Resources>
Then in my HubSections, I want to use the above template to display data, like such:
<HubSection Width="350" x:Uid="Test" Header="Test">
<DataTemplate>
<ListBox x:Name="TestListBox" Grid.Column="1"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource BaseGridTemplate}"
>
</ListBox>
</DataTemplate>
</HubSection>
In my UI, both properties (PlayerName and OtherProperty) are displayed correctly. But what I would like to do is to bind to a different property for the TextBox (i.e. different from OtherProperty).
I really have no clue on how I should proceed, or if it even is possible.
I thought I could to something like defining my TextBox in the Resources section like such :
<TextBox Text="{Binding, Mode=TwoWay}">
And then Hope I could add something in the HubSection part ?
Assuming you have a way of knowing when you have to use your different properties I think the best way for you to do this is to use a template selector and have different kind of templates based on what you want to display.
Another alternative would be to use a value converter for TextBox and use the binding you suggested:
<TextBox Text="{Binding, Mode=TwoWay, Converter={StaticResource YourConverter}">
These are just some ideas to get you started, another important thing to know is if are you trying to use a different property on the same object?

C# WPF Binding to DataBase

Hei all. If something would be not clear then please tell
I have dataGrid and TreeView.
I have loaded data base as Entity Data Model and some tables.
One of these tables "relation" should show to the datagrid. But its (relation table) column depend of the other tables as system,model,function and device. In the Data grid should be 4 columns which contain names of these system,model,function and device. (the picture 1 as should be)
Problem in the how it all show. DataSource don't work well... see picture 2.
<Grid DataContext="{StaticResource relationsViewSource}">
<DataGrid AutoGenerateColumns="True" Name="gridInventory" HorizontalContentAlignment="Right" Margin="255,12,12,128" ItemsSource="{Binding}" />
<StackPanel Height="391" HorizontalAlignment="Left" Margin="10,10,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="239" DataContext="{StaticResource systemsViewSource}" >
<TreeView Height="391" Name="treeView1" Width="239" VerticalContentAlignment="Top" HorizontalAlignment="Left" VerticalAlignment="Top" ItemsSource="{Binding}" />
</StackPanel>
</Grid>
Picture 1:
Picture2:
You are binding both the TreeView and some of your DataGrid columns to an object, but not telling WPF how to draw the object. When WPF doesn't know how to draw an object, it by default draws it using a TextBlock with the Text bound to the object's .ToString()
You need to set the ItemTemplate to tell WPF how to draw your individual objects, such as this:
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.ItemTemplate>
You could also use an implicit DataTemplate to tell WPF how to draw specific objects. This is just a DataTemplate that specifies a DataType without a Key, and WPF will use it anytime it tries to render an object of the specified type.
If you want to avoid removing AutoGenerateColumns="True" and manually specifying your DataGrid's columns, this is probably the method to use.
<DataGrid.Resources>
<DataTemplate DataType="{x:Type local:Device}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</DataGrid.Resources>
The TreeView you would need to set the Path on the Binding to the property you want to display,
The Devices is a collection and you need to put a Listview in there or something that will display a collection and then put a DataTemplate on it to display what you need, either that or bind to a converter to return a static string representation of the Device list
Or just get someone to do it all for you as seems the case on here lol

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

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...

Adding a row of controls in a Silverlight Datagrid

In the application I'm developing, I'm using a datagrid to display data fetch from a database. It is declared like so in my XAML file:
<sdk:DataGrid x:Name="dg" AutoGenerateColumns="False" ItemSource={Binding etc.} >
<sdk:DataGrid.Columns>
<sdk:DataGridTextBoxColumn Header="Col1"
Binding="{Binding Col1Data}" />
<sdk:DataGridTextBoxColumn Header="Col2"
Binding="{Binding Col2Data}" />
<sdk:DataGridTextBoxColumn Header="Col3"
Binding="{Binding Col3Data}" />
<sdk:DataGridCheckBoxColumn Header="Col4"
Binding="{Binding Col4Data}" />
<sdk:DataGrid.Columns>
<sdk:DataGrid>
What I want to do, is to add a row, containing 5 combo boxes (1 for each column) between the header and the first row of my data. It is pretty easy to do for a column, using DataGridTemplateColumn, but how can I do this for a row?
Thanks in advance for your suggestions!
Fake edit: Oh by the way I'm not a fan of code behind (trying to go full MVVM), so I'm looking for a way to do this in XAML, if possible.
You may be able to get away with providing a template for the header, but failing that you'll need to re-template the DataGrid in order to do this.
Ok, so I kinda found a work around. I declared a template for my cells that contains a button and a textblock bound to the data. i bind the visibility property of the button on a boolean that will be true only for the elements of the first row.
<sdk:DataGridTemplateColumn Header="Col1" Width="60">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Height="30">
<Button Content="Boutton" Visibility="{Binding Path=IsFirstElement, Converter={StaticResource visibilityConverter}}" />
<TextBlock Text="{Binding Path=Col1Data}" />
</StackPanel>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
It's a bit hack-ish. But it works. My only concern is performance since a button is declared for each cell. So with thousands of row, I guess there could be a performance hit.

How to conditionally hide elements in a TemplateColumn of a WPF DataGrid?

Context: a C# 4.0 WPF application with a datagrid that has one TemplateColumn showing a progress bar.
How can one get the grid to only display the progress bar for certain items based on a condition?
Maybe listening to the events and hiding the cells / setting visibile to false would be an option.
This is how it looks right now (the progress bar is shown for all items):
<UserControl.Resources>
<DataTemplate x:Key="PotentialDataTemplate">
<Grid Width="70">
<ProgressBar
Height="12"
VerticalAlignment="Center"
Value="{Binding Path=Potential, Mode=OneWay}" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<DataGrid x:Name="dataGrid"
ItemsSource="{Binding Path=Items}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn
Header="{Binding Source={x:Static text:TextBindingProvider.Instance}, Path=CompendiumHeaderPotential}"
Width="Auto"
MinWidth="80"
CellTemplate="{StaticResource PotentialDataTemplate}"
IsReadOnly="true"
SortMemberPath="Potential" />
</DataGrid.Columns>
</DataGrid>
You have a couple of options depending on what the conditions are for visibility. If you have a separate property such as "IsPotentialVisible" then you can bind this to the Visibility property of the progressbar using a BooleanToVisibilityConverter.
Next up, if it is a simple condition such as "hide when Potential == 0", then you could create a DataTrigger that handles this condition.
Otherwise you can also create a custom converter that spits out a visibility based on whatever input properties / parameters are required.
Just found an answer, I simply add the Visibility attribute and Bind it to some conditional Logic in the ViewModel.
Visibility = "{Binding Path=ShowPotentialBar, Mode=OneWay}
So:
<Grid Width="70">
<ProgressBar
Height="12"
VerticalAlignment="Center"
Value="{Binding Path=Potential, Mode=OneWay}"
Visibility = "{Binding Path=ShowPotentialBar, Mode=OneWay}" />
</Grid>

Categories