I'm using WPFLocalizationExtension to localize a C#/ .Net4.5 application but I didn't manage to localize DropDown Menus with custom DataTemplate because I can't use the DisplayMemberPath. For ordinary dropdown localization works like this:
<telerik:RadComboBox ItemsSource="{Binding GlassColors}"
SelectedValue="{Binding Ampule.ID_GlassColor, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="ID_GlassColor"
DisplayMemberPath="{lang:Loc dmp_GlassColor}"/>
The ComboBox example above is linked to a database table containing to language. The displayed language is changed by the localized DisplayMemberPath. This approach is very easy and I can recommend it to everyone else. How ever it doesn't work for comboboxes using a custom DataTemplate. See example below:
<telerik:RadComboBox ItemsSource="{Binding PackagingTypesFilter}"
SelectedValue="{Binding SelectedPackagingTypeFilter}"
SelectedValuePath="ID_PackagingType">
<telerik:RadComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type model:Tbl_PackagingMaster_ID_PackagingType}">
<Grid VerticalAlignment="Center" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding TypeIcon, Converter={StaticResource StringToUriConverter}}" VerticalAlignment="Top" Grid.Column="0" Height="14" Margin="2" />
<TextBlock Text="{Binding PackagingTypeDescription}" Style="{StaticResource TextBlockMediumSmallBlackStyle}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
The ItemSource is a Collection where PackagingTypeDescription contains the English and PackagingTypeDescriptionGerman contains the German description.
How can I localize the code sample above?
I solved this using a TemplateSelector.
Here is the TemplateSelector class:
using System.Windows;
using System.Windows.Controls;
namespace Common.TemplateSelector
{
public class LanguageTemplateSelector : DataTemplateSelector
{
public DataTemplate TemplateEnglish { get; set; }
public DataTemplate TemplateGerman { get; set; }
public const string LanguageIdentifier = "de";
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return Configuration.Configuration.Language == LanguageIdentifier ? this.TemplateGerman : this.TemplateEnglish;
}
}
}
And here the template definition and the definition of the TemplateSelector defined in the <UserControl.Resources></UserControl.Resources> area:
<DataTemplate DataType="{x:Type model:Tbl_PackagingMaster_ID_PackagingType}" x:Key="PackagingTypeEnglish">
<Grid VerticalAlignment="Center" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding TypeIcon, Converter={StaticResource StringToUriConverter}}" VerticalAlignment="Top" Grid.Column="0" Height="14" Margin="2" />
<TextBlock Text="{Binding PackagingTypeDescription}" Style="{StaticResource TextBlockMediumSmallBlackStyle}" Grid.Column="1"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type model:Tbl_PackagingMaster_ID_PackagingType}" x:Key="PackagingTypeGerman">
<Grid VerticalAlignment="Center" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding TypeIcon, Converter={StaticResource StringToUriConverter}}" VerticalAlignment="Top" Grid.Column="0" Height="14" Margin="2" />
<TextBlock Text="{Binding PackagingTypeDescriptionGerman}" Style="{StaticResource TextBlockMediumSmallBlackStyle}" Grid.Column="1"/>
</Grid>
</DataTemplate>
<templateSelector:LanguageTemplateSelector x:Key="PackagingTypeLanguageSelector"
TemplateEnglish="{StaticResource PackagingTypeEnglish}"
TemplateGerman="{StaticResource PackagingTypeGerman}" />
This is how I use it for the ComboBox:
<telerik:RadComboBox ItemsSource="{Binding PackagingTypesFilter}"
SelectedValue="{Binding SelectedPackagingTypeFilter}"
SelectedValuePath="ID_PackagingType"
ItemTemplateSelector="{StaticResource PackagingTypeLanguageSelector}" />
However, I'm still looking for a more elegant way to solve this.
Related
I'm doing some fixes in a project where I need to edit an entity named Tariff, which have as a property a collection of TariffSteps. The model was made with Entity Framework Code First:
namespace pbxControl.model.Classes
{
public class Tariff : PropertyChangedNotifier
{
public int TariffId { get; set; }
public string Description { get; set; }
// ...
public ObservableCollection<TariffStep> TariffSteps { get; set; }
// ...
}
public class TariffStep : PropertyChangedNotifier
{
public int TariffStepId { get; set; }
[DefaultValue(0)]
public int Duration { get; set; }
// ...
#region references
public virtual ObservableCollection<Tariff> Tariffs { get; set; }
// ...
Which created a many to many relationship in the DB through an intermediate table "TariffStepTariffs".
In the UI the interface is based in a master - detail scenario where an outer grid is binded to a CollectionViewSource (on tariff collection), inside that grid there is a Datagrid where the tariffs are shown (master), and another grid where the selected tariff is detailed.
Originally, before the new fixes were implemented, the way of dealing with the tariffSteps was as follow: an area for add / remove/ modify TariffSteps in the Model (TariffStepsGrid), and another area where the client could add tariffSteps from the first area to a Tariff (TariffTariffStepsGrid). The idea was that several tariff could share the same step if they have the same properties:
previous way of assign tariffSteps to tariff
<TabItem Name="Tariff_Tab" HorizontalAlignment="Left" TabIndex="5" Header="Tarifas" Style="{StaticResource HorizontalTab}">
<Grid x:Name="TariffsGrid" DataContext="{Binding Source={StaticResource tariffViewSource}}">
<Grid x:Name="TariffDetailGrid" Grid.Row="0" Grid.Column="0" DataContext="{Binding}">
<Grid x:Name="TariffDetailPropertiesGrid" Grid.Row="0" Grid.Column="0" DataContext="{Binding}">
<!-- Some controls for properties of the Tariff -->
</Grid>
<Grid x:Name="CurrencyGrid" Grid.Row="0" Grid.Column="1" DataContext="{StaticResource currencyViewSource}">
<!-- This grid is for management of another entity (Currency) -->
</Grid>
<Grid Grid.Row="1" Grid.Column="0" x:Name="TariffTariffStepsGrid" HorizontalAlignment="Right">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="360"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<Grid x:Name="TariffTariffStep" Grid.Column="0" DataContext="{Binding Path=TariffSteps}" HorizontalAlignment="Right">
<ListBox x:Name="TariffStepList1" ItemsSource="{Binding}" HorizontalAlignment="Right" VerticalAlignment="Top"
VerticalContentAlignment="Center" HorizontalContentAlignment="Left" Height="130" Width="200" Margin="0 15 0 5"
TabIndex="22">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="2 2">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=DurationV, Mode=TwoWay}" Grid.Column="0" Width="60" TextAlignment="Left"/>
<TextBlock Text="{Binding Path=PeriodV, Mode=TwoWay}" Grid.Column="1" Width="60" TextAlignment="Center"/>
<TextBlock Text="{Binding Path=CostV, Mode=TwoWay}" Grid.Column="2" Width="60" TextAlignment="Right"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
<Grid x:Name="TariffTariffStepsButtons" Grid.Column="1">
<Grid Height="50">
<Button x:Name="TariffTariffStepAdd" HorizontalAlignment="Center" VerticalAlignment="Top"
Click="TariffTariffStepAdd_Click" Background="Transparent" Style="{StaticResource LessThanButton}">
</Button>
<Button x:Name="TariffTariffStepRemove" HorizontalAlignment="Center" VerticalAlignment="Bottom"
Click="TariffTariffStepRemove_Click" Background="Transparent" Style="{StaticResource GreaterThanButton}">
</Button>
</Grid>
</Grid>
</Grid>
<Grid x:Name="TariffStepsGrid" Grid.Row="1" Grid.Column="1" DataContext="{StaticResource tariffStepViewSource}">
<!-- Some controls for properties of the TariffStep and buttons for add / remove a TariffStep -->
<ListBox x:Name="TariffStepList" ItemsSource="{Binding}" HorizontalAlignment="Right" VerticalAlignment="Top"
VerticalContentAlignment="Center" HorizontalContentAlignment="Left" Height="130" Width="200" Margin="0 15 20 5"
SelectionChanged="TariffStep_SelectionChanged" GotFocus="TariffStepList_GotFocus" TabIndex="22">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="2 2">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=DurationV, Mode=TwoWay}" Grid.Column="0" Width="60" TextAlignment="Left"/>
<TextBlock Text="{Binding Path=PeriodV, Mode=TwoWay}" Grid.Column="1" Width="60" TextAlignment="Center"/>
<TextBlock Text="{Binding Path=CostV, Mode=TwoWay}" Grid.Column="2" Width="60" TextAlignment="Right"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
<Grid x:Name="TariffActionGrid" Grid.Row="1" Grid.Column="0"><!-- Buttons for save, etc. --> </Grid>
<Grid x:Name="AllTariffsGrid" Grid.Row="2" Grid.Column="0" VerticalAlignment="Top" Grid.ColumnSpan="2">
<DataGrid x:Name="ListTarif1" HorizontalAlignment="Right" Height="255" VerticalAlignment="Top" AutoGenerateColumns="False"
ItemsSource="{Binding}" Width="760" Margin="20,25,20,0" SelectionChanged="ListTarif1_SelectionChanged"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="DescripciĆ³n" Binding="{Binding DescriptionV, Mode=TwoWay}" IsReadOnly="True" Width="152"/>
<DataGridTextColumn Header="Fecha de Inicio" Binding="{Binding StartDateV}" IsReadOnly="True" Width="152"/>
<DataGridTextColumn Header="Fecha Final" Binding="{Binding EndDateV}" IsReadOnly="True" Width="152"/>
<DataGridTextColumn Binding="{Binding CurrencyV.Code}" Header="Moneda" IsReadOnly="True" Width="152"/>
<DataGridTextColumn Binding="{Binding InitialCostV}" IsReadOnly="True" Width="152">
<DataGridTextColumn.Header>
<AccessText TextWrapping="WrapWithOverflow" Width="100" TextAlignment="Center">Costo Inicial</AccessText>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</TabItem>
For populating the TariffStepList listbox (the one for the collection of all tariffs) in code behind was implemented a filter that allowed in the view of the tariffStepViewSource only the tariffsteps that were not already assigned to the tariff:
public void TariffStepsNonInTariffFilter(object sender, FilterEventArgs e)
{
TariffStep tariffStep = e.Item as TariffStep;
var Tariff = ListTarif1.SelectedItem as Tariff;
if (Tariff != null)
{
if (tariffStep.Tariffs.Contains(Tariff))
{
e.Accepted = false;
return;
}
}
e.Accepted = true;
}
That filter was assigned to the tariffStepViewSource only when the app has the Tariff tab on focus, when the focus were to another tab, it was unasigned.
Well, all that worked like a charm, but in the new design there was no need for the tariffStepViewSource because the tariffSteps were assigned directly to the tariff (it didn't matter that they were repeated in the database). As can be seen in the following xaml, the binding is to the collection of tariffSteps of the tariff ( DataContext="{Binding Path=TariffSteps} ).
<TabItem Name="Tariff_Tab" HorizontalAlignment="Left" TabIndex="5" Header="Tarifas" Style="{StaticResource HorizontalTab}">
<Grid x:Name="TariffsGrid" DataContext="{Binding Source={StaticResource tariffViewSource}}">
<Grid x:Name="TariffDetailGrid" Grid.Row="0" Grid.Column="0" DataContext="{Binding}">
<!-- grids & controls for properties of the Tariff & the Currency entity -->
<Grid x:Name="TariffTariffStepsGrid" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right"
DataContext="{Binding Path=TariffSteps}">
<TextBox x:Name="TariffStepCost" HorizontalAlignment="Right" VerticalAlignment="Top" VerticalContentAlignment="Center"
HorizontalContentAlignment="Right" Margin="0,115,225,0" Height="25" Width="100"
Text="{Binding Path=CostV, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat=N2}"
LostFocus="TariffStepCost_LostFocus" TabIndex="21"
IsEnabled="{Binding DataContext.Source.Count, Converter={StaticResource IsEnabledBySourceCountConverter},
RelativeSource={RelativeSource Self}}"/>
<Label x:Name="TariffStepCostlabel" Height="25" Content="Costo:" Margin="0,115,325,0" VerticalAlignment="Top"
VerticalContentAlignment="Center" HorizontalAlignment="Right" HorizontalContentAlignment="Left" Width="55" Padding="0"/>
<!-- Some more controls for other properties of the TariffStep and buttons for add / remove a TariffStep -->
<ListBox x:Name="TariffStepList1" ItemsSource="{Binding Path=TariffSteps}" HorizontalAlignment="Right" VerticalAlignment="Top"
VerticalContentAlignment="Center" HorizontalContentAlignment="Left" Height="130" Width="200" Margin="0 15 20 5"
TabIndex="22">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="2 2">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=DurationV, Mode=TwoWay}" Grid.Column="0" Width="60" TextAlignment="Left"/>
<TextBlock Text="{Binding Path=PeriodV, Mode=TwoWay}" Grid.Column="1" Width="60" TextAlignment="Center"/>
<TextBlock Text="{Binding Path=CostV, Mode=TwoWay}" Grid.Column="2" Width="60" TextAlignment="Right"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
<!-- TariffActionGrid && AllTariffsGrid -->
</Grid>
</TabItem>
The fact is that, no matter what I do, if I remove the filter from code behind, in the listbox "TariffStepList1" nothing get shown. I can add new tariffSteps and save the context, but when I close the app and open it again, nothing is shown. I've tested all option, even creating new ListBox, button, etc. in a different tab. I have checked that there is no remaining references to tariffStepViewSource in the whole project, still there is no way the tariffSteps get shown.
In another tab I have the same xaml controls structure in which a navigation property is modified with exactly the same master - detail scenario (an entity named Extension which have a collection of associated email addresses), and there is no problem there. As soon as I re-incorporate the tariffStepViewSource, with it corresponding filter, the tariffSteps get shown, even when there binding in the Grid is not to the viewsource.
I have an odd problem with TextBox with TextTrimming set to CharacterElipsis used in a DataTemplate in WPF application. At the start of application everything works fine. But when I am resizing the window - reducing the width, the trimming is not working.
In an example below:
<Grid>
<DockPanel>
<DockPanel.Resources>
<DataTemplate x:Key="lowerLevel" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="34*" />
<ColumnDefinition Width="26*" />
<ColumnDefinition Width="26*" />
<ColumnDefinition Width="14*" />
</Grid.ColumnDefinitions>
<TextBlock Text="textboxvalue1" Grid.Column="0" FontWeight="Bold" VerticalAlignment="Center" Margin="15,10,0,10" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" />
<TextBlock Text="textboxvalue2" Grid.Column="1" FontWeight="Bold" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" />
<TextBlock Text="textboxvalue3" Grid.Column="2" FontWeight="Bold" VerticalAlignment="Center" Margin="0,10,0,10" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" />
<CheckBox IsChecked="True" Content="ApprovedText" FontWeight="Bold" Grid.Column="3" VerticalAlignment="Center" Margin="0,10,15,10" />
</Grid>
</DataTemplate>
</DockPanel.Resources>
<ListView x:Name="listViewControl" ItemTemplate="{StaticResource lowerLevel}" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" />
</DockPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="34*" />
<ColumnDefinition Width="26*" />
<ColumnDefinition Width="26*" />
<ColumnDefinition Width="14*" />
</Grid.ColumnDefinitions>
<TextBlock Text="textboxvalue1" Grid.Column="0" FontWeight="Bold" VerticalAlignment="Center" Margin="15,10,0,10" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" />
<TextBlock Text="textboxvalue2" Grid.Column="1" FontWeight="Bold" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" />
<TextBlock Text="textboxvalue3" Grid.Column="2" FontWeight="Bold" VerticalAlignment="Center" Margin="0,10,0,10" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" />
<CheckBox IsChecked="True" Content="ApprovedText" FontWeight="Bold" Grid.Column="3" VerticalAlignment="Center" Margin="0,10,15,10" />
</Grid>
</Grid>
I am having two identical Grids, but first one is placed in a DataTemplate and the second is just a separate control. TextBoxes in bottom grid are trimming correctly, when resizing window, however the TextBoxes from DataTemplate do not fit the width of parent columns while window resizing.
The code behind is:
public partial class MainWindow : Window
{
private ListCollectionView comparedFamiliesView;
public ListCollectionView ComparedFamiliesView
{
get
{
if (comparedFamiliesView == null)
{
comparedFamiliesView = new ListCollectionView(new List<Object>() { new Object(), new Object(), new Object() });
}
return comparedFamiliesView;
}
}
public MainWindow()
{
InitializeComponent();
listViewControl.ItemsSource = ComparedFamiliesView;
}
}
It basically adds three objects to have something to view on ListView.
I was trying different combinations of VerticalAlignment, VerticalContentAlignment - didn't work.
What I have tried was to place each TextBox in separate Grid in order to bind its Width to TextBox Width or MaxWidth, like:
<Grid Grid.Column="0" x:Name="grid1">
<TextBlock Text="textboxvalue1" FontWeight="Bold" VerticalAlignment="Center" Margin="15,10,0,10" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" MaxWidth="{Binding ActualWidth, ElementName=grid1}" />
</Grid>
Didn't work either.
How can I force TextBoxes in DataTemplate to behave the same way as those in separate Grid?
Or what am I doing wrong with TextTrimming when using in DataTemplate.
Thank you for your help!!!
Regards,
Ariel
Try to set the ScrollViewer.HorizontalScrollBarVisibility attached property of the ListView to Disabled:
<ListView x:Name="listViewControl" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ...
I have using the itemscontrol in WPF, I have given the dictionary collection as itemsource for itemscontrol. In this dictionary collection, will be used key and observablecollection. Different items will be in observablecollection of each dictionary items. so, when i'm given an itemsource it will be taken same height.
see the code:
<ItemsControl
Grid.Row="1"
Height="Auto"
ItemsSource="{Binding Values}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
IsItemsHost="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<GroupBox
MinWidth="303"
Margin="5,0,0,0">
<ItemsControl Margin="20,5,0,5">
<ItemsControl.Resources>
<CollectionViewSource x:Key="Collection" Source="{Binding Value}" />
<DataTemplate DataType="{x:Type Model:Sensor}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="1"
Content="{Binding Name}"
FontFamily="SegoeUI-Semibold"
FontSize="12"
FontWeight="SemiBold" />
<Label
Grid.Column="2"
HorizontalContentAlignment="Center"
Content="{Binding Value}"
FontFamily="SegoeUI"
FontSize="12" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type Model:DigitalInput}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="1"
Content="{Binding Name}"
FontFamily="SegoeUI-Semibold"
FontSize="12"
FontWeight="SemiBold" />
<Label
Grid.Column="2"
HorizontalContentAlignment="Center"
Content="{Binding InputState}"
FontFamily="SegoeUI"
FontSize="12" />
</Grid>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource Collection}}" />
</CompositeCollection>
</ItemsControl.ItemsSource>
</ItemsControl>
</GroupBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
see the class code:
private Dictionary<string, ObservableCollection<IValue>> values;
public Dictionary<string, ObservableCollection<IValue>> Values
{
get { return values; }
set { values = value; }
}
Current output:
Expected output:
I need to group the items as an expected output, so could you please provide any solution to achieve that?
This is how WrapPanel works. If you set Horizontal all items in row will have same height and it wraps elements to the next row.
You can try specifying Orientation="Vertical" for your WrapPanel, but not quite sure if it suits you. In this case all elements in column will have same width.
Otherwise you don't need either WrapPanel or UniformGrid, you need different panel which is called StaggeredPanel. Source code for uwp can be easily used in WPF, I just checked it.
Only had to rewrite one line which is not a big deal with the following answer: RegisterPropertyChangedCallback(Panel.HorizontalAlignmentProperty, OnHorizontalAlignmentChanged);
An explanation for similar control can be found on codeproject (Called VariableSizedWrapGrid). But I checked it and it has errors somewhere.
On ios it's called mosaic view or StaggeredLayoutManager for RecyclerView on Android.
Instead of WrapPanel, try a UniformGrid:
<UniformGrid Columns="1" IsItemsHost="True" />
Also, I'm not sure about the Height="Auto" setting. Remove it. The setting belongs to the RowDefinition of the grid.
I have a TabControl in my app. I'd like to have as many TabItems as many entries are in my dictionary.
Here's my dictionary:
public Dictionary<string , ObservableCollection<PerformanceCounter>> Counters
{
get { return _Counters; }
}
Dictionary<string, ObservableCollection<PerformanceCounter>> _Counters = new Dictionary<string , ObservableCollection<PerformanceCounter>>();
Every entry has a string key and ObservableCollection of PerformanceCounter objects. Important thing is the fact that every PerformanceCounter object has properties: CounterName and InstanceName - I'll need these two to display them.
Now, to my XAML:
<TabItem Header="Memory">
<Grid Name="RAMGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Name="RAMListBox" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" ItemsSource="{Binding Memory, Mode=OneWay}" SelectionMode="Multiple" BorderThickness="1" BorderBrush="#FF8B8B8B" SelectionChanged="RAMListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding CounterName, Mode=OneWay}" />
<Run Text="{Binding InstanceName, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="RAMSelectAllButton" Margin="0,10,0,0" Grid.Column="0" Grid.Row="1" Click="RAMSelectAllButton_Click" >
<TextBlock Text="SELECT ALL"/>
</Button>
<Button Name="RAMUnSelectAllButton" Margin="0,10,0,0" Grid.Column="1" Grid.Row="1" Click="RAMUnSelectAllButton_Click" >
<TextBlock Text="UNSELECT ALL"/>
</Button>
</Grid>
</TabItem>
That's what I did and, as you might already know, it does not work. The above code is only for one entry of my dictionary, where the key is "Memory".
In my code I set DataContext:
this.DataContext = appData.Counters;
appData.Counters is that dictionary I presented at the beginning.
Here's what I'd like to achieve:
No matter how many entries there are in my dictionary, my TabControl would display TabItem for each of them.
Each TabItem has a ListBox and 2 buttons. I'll need too be able to access those (in order to clear the list and to have click event for each button).
I really don't know how to do it, I hope you can help me out.
Binding TabControl to items in Dictionary:
<Window.Resources>
<DataTemplate x:Key="templateForTheContent" >
<StackPanel>
<ListBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0"
ItemsSource="{Binding Value, Mode=OneWay}"
SelectionMode="Multiple"
BorderThickness="1" BorderBrush="#FF8B8B8B">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding CounterName, Mode=OneWay}" />
<Run Text="{Binding InstanceName, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="templateForTheHeader" >
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl TabStripPlacement="Left" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Counters}"
ContentTemplate="{StaticResource templateForTheContent}"
ItemTemplate="{StaticResource templateForTheHeader}">
</TabControl>
</Grid>
Now, Dictionary is not observable so if items will be added/removed during runtime, you may consider using something like ObservableDictionary instead
Create a ViewModel-class containing:
your Dictionary
two ICommand-Implementations for your Buttons
then
set the ViewModel-class as DataContext of the TabControl
set Counters as the ItemSource of the TabControl
reuse your XAML-Code defined within the TabItem and use it as
the Tabcontrol.ContentTemplate
Bind .Command of your Buttons to the ICommands in your ViewModel using RelativeSource
see for samples:
ContentTemplate: https://wpf.2000things.com/tag/tabcontrol/
ICommand https://stackoverflow.com/a/1468830/4919708
RelativeSource: https://stackoverflow.com/a/84317/4919708
As i said in one of the comments above I changed my Dictionary to this:
//list of all counters
public ObservableCollection<ObservableCollection<PerformanceCounter>> Counters
{
get { return _Counters; }
}
ObservableCollection<ObservableCollection<PerformanceCounter>> _Counters = new ObservableCollection<ObservableCollection<PerformanceCounter>>();
i used #Arie's solution to write this XAML:
<DataTemplate x:Key="templateForTheContent" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0"
ItemsSource="{Binding}"
SelectionMode="Multiple"
BorderThickness="1" BorderBrush="#FF8B8B8B">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding CounterName, Mode=OneWay}" />
<Run Text="{Binding InstanceName, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="RAMSelectAllButton" Margin="0,10,0,0" Grid.Column="0" Grid.Row="1" >
<TextBlock Text="SELECT ALL"/>
</Button>
<Button Name="RAMUnSelectAllButton" Margin="0,10,0,0" Grid.Column="1" Grid.Row="1" >
<TextBlock Text="UNSELECT ALL"/>
</Button>
</Grid>
</DataTemplate>
<DataTemplate x:Key="templateForTheHeader" >
<TextBlock Text="{Binding CategoryName}"/>
</DataTemplate>
</Window.Resources>
It displays correctly as many Tabs as I add entries to my Class ObservableCollection in the code behind.
Now i have a new problem: I don't know how to access each listBox from each Tab. i need to be able to read the list of selected objects.
I am have a text which is has bold, underline and italic html characters. For example
<b> hello<b> how are <i>you</i>. I am <u>fine</u>
I have to show it in formatted form in a textblock on WP7. I have a listbox like this
<ListBox x:Name="LBayaDetail" Loaded="LBayaDetail_Loaded" Margin="6,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="ayaContent" Margin="0,6,0,0" Hold="ayaContent_Hold" Tap="ayaContent_Tap" Loaded="ayaContent_Loaded" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="6"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Background="#FFC5AC88" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock x:Name="ayaIndex" Text="{Binding aya}" FontSize="36" Margin="0" FontWeight="Bold" HorizontalAlignment="Center" />
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Center">
<Image Source="{Binding BookmarkImage}" HorizontalAlignment="Center" Width="48" Height="48" Margin="0,0,0,12" />
<Image Source="{Binding NoteImage}" HorizontalAlignment="Center" Width="48" Height="48" Margin="0,0,0,12" />
<Image Source="{Binding TagImage}" HorizontalAlignment="Center" Width="48" Height="48" Margin="0,0,0,12" />
</StackPanel>
</Grid>
<Grid Grid.Row="1" Background="#FFC5AC88" x:Name="Media" Tap="Media_Tap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image Source="/Images/Media-Play(1).png" Width="30" Height="30" HorizontalAlignment="Center" Margin="12,0,0,0" VerticalAlignment="Top" />
</Grid>
<!--ini pak dimana tempat untuk ayat dan translasi-->
<Grid Grid.Column="2" Background="#FFAC9574" Margin="6,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock x:Name="aya" TextWrapping="Wrap" Text="{Binding text}" HorizontalAlignment="Right" FontFamily="/Fonts/me_quran2.ttf#me_quran2" FontSize="{Binding FontSizeAya}" Foreground="Black" Margin="24,0,12,-12" TextAlignment="Right" Visibility="{Binding visibility1}" />
</Grid>
<Grid Grid.Column="2" Grid.Row="1" Margin="6,0,0,0" Background="#FFAC9574" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel>
<TextBlock Visibility="{Binding visibility2}" x:Name="translation" Text="{Binding translation}" TextWrapping="Wrap" HorizontalAlignment="Right" FontFamily="/Fonts/ARIALUNI.TTF#Arial Unicode MS" FontSize="{Binding FontSizeTranslation}" Foreground="#FF5D2A07" Margin="12,6,6,0" />
<TextBlock Visibility="{Binding visibility3}" x:Name="translation2" Text="{Binding translation2}" TextWrapping="Wrap" HorizontalAlignment="Right" FontFamily="/Fonts/ARIALUNI.TTF#Arial Unicode MS" FontSize="{Binding FontSizeTranslation}" Foreground="DarkGreen" Margin="12,20,6,0" />
</StackPanel>
</Grid>
<!-- -->
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I fetch from database like
App.Listxyz = (Application.Current as App).db.SelectList<Aya>(strSelect);
And assign it to Listbox like this
LBayaDetail.ItemsSource = App.ListAyaBySurah;
And shows the text as it is and do not format it which is obvious. I searched for it and I was able to format individual textblock by using "RUN" but I am unable to do it in listbox.
I also tried to use HTMLTextBlock but it also doesn't format the text and shows it like this
Hi
How
Are
You
Any help will be much appreciated that how do I format a textblock with different text decorations.
Thanks
You should place a grid and a stackpanel inside the listbox. Something like the following
<Grid>
<StackPanel Grid.Column="1">
<TextBlock Padding="0,5,0,2" TextWrapping="Wrap">
<Run Text="{Binding test}" FontWeight="Bold" /> <Run Text="{Binding test2}" />
<LineBreak/>
<Run Text="{Binding test3}" />
</TextBlock>
</StackPanel>
</Grid>
One way to do that is:
public class FormattedText
{
public string Text { get; set; }
public bool IsBold { get; set; }
public bool IsItalic { get; set; }
public bool IsUnderlined { get; set; }
}
Have a method that converts the HTML you stored in your db to a list of the class you have above
example:
From this:
<b> hello<b> how are <i>you</i>. I am <u>fine</u>
to this:
First element:
Text = "hello"
IsBold = true
skip what not needed since bool default value is false
Second element
Text =" how are"
skip what not needed since bool default value is false
Third item
Text ="you"
IsItalic=true;
skip what not needed since bool default value is false
and so on....
and then have another method that from that list creates a list of Runs to be added to your TextBlock or
maybe create a custom TextBlock witch take the List<FormattedText> from DataContext and process it by adding the Run elements to self