using a datatemplate to dynamically load radpanes and property grids - c#

I'm using some telerik components and am trying to do the following:
I have a TabCollection which is a observable collection of ConfigurationTab I want to create a radPane for every ConfigurationTab in this collection (this i have managed sort of) then i want the current tab to display a RadPropertyGrid to which I will bind a (custom) collection of properties.
The code is as follows:
(XAML)
<UserControl.Resources>
<DataTemplate x:Key="TabCollectionTemplate">
<telerik:RadPane CanUserClose="False" Header="{Binding DisplayName}">
<telerik:RadPropertyGrid Margin="0,4,0,4"
assistant:PropertyGridAssistant.AllowDescription="True"
assistant:PropertyGridAssistant.AllowReset="True"
valid:ValidationAssistant.IsEnabled="True"
valid:ValidationAssistant.IsValid="{Binding IsTaskValid,
Mode=OneWayToSource}"
BorderThickness="0,0,0,0"
DockPanel.Dock="Top"
EnableEditorCaching="False"
Item="{Binding Path=TabPropertyCollection,
UpdateSourceTrigger=PropertyChanged}"
SearchBoxVisibility="Collapsed"
SortAndGroupButtonsVisibility="Visible" />
</telerik:RadPane>
</DataTemplate>
</UserControl.Resources>
<Grid>
<telerik:RadDocking Name="ConfigurationDocking">
<telerik:RadDocking.DocumentHost>
<telerik:RadSplitContainer InitialPosition="DockedRight">
<telerik:RadPaneGroup ItemTemplate="{StaticResource TabCollectionTemplate}" ItemsSource="{Binding TabCollection}" />
</telerik:RadSplitContainer>
</telerik:RadDocking.DocumentHost>
</telerik:RadDocking>
</Grid>
C#
public class ConfigurationTab : ObservableObject
{
private string mDisplayName = string.Empty;
private Property.Management.Properties mProperties = new Property.Management.Properties();
public string DisplayName
{
get
{
return mDisplayName;
}
set
{
mDisplayName = value;
this.RaisePropertyChanged(() => this.DisplayName);
}
}
public Property.Management.Properties TabProperties
{
get
{
return mProperties;
}
set
{
mProperties = value;
this.RaisePropertyChanged(() => this.TabProperties);
this.RaisePropertyChanged(() => this.TabPropertyCollection);
}
}
public PropertiesToPropertyGridAdapter<DescriptorOfProperty> TabPropertyCollection
{
get
{
return new PropertiesToPropertyGridAdapter<DescriptorOfProperty>(mProperties);
}
}
The thing i get using the current code looks as follows, the headers of the tabs are filled in correctly with the display name but the part where the content of the pane only ever displays: "IOLAN.ModuleConfigurationTester.ConfigurationTab" which is the problem:
Also when I click on the 2nd tab I get a null reference exception:
System.NullReferenceException occurred
Message: Exception thrown: 'System.NullReferenceException' in Telerik.Windows.Controls.Docking.dll
Additional information: Object reference not set to an instance of an object.
Can anyone see what i'm doing wrong?
EDIT
An interesting thing to add, if i change my data template to a simple textbox It changes nothing, the only thing i see in the radpane = "IOLAN.ModuleConfigurationTester.ConfigurationTab" and i still get a null reference trying to open the other tab:
<DataTemplate x:Key="TabCollectionTemplate">
<telerik:RadPane CanUserClose="False" Header="{Binding DisplayName}">
<TextBox Text="Hallo" />
</telerik:RadPane>
</DataTemplate>

After a while i figured out what I had to do, i simply had to use a TablControl and set the content template, doing it like this solved all the problems:
<UserControl.Resources>
<DataTemplate x:Key="ContentTemplate">
<telerik:RadPropertyGrid Name="PropertyGrid"
Margin="0,4,20,4"
assistant:PropertyGridAssistant.AllowDescription="True"
assistant:PropertyGridAssistant.AllowReset="True"
BorderThickness="0,0,0,0"
IsGrouped="True"
Item="{Binding Path=TabPropertyCollection,
UpdateSourceTrigger=PropertyChanged}"
SearchBoxVisibility="Collapsed" />
</DataTemplate>
<DataTemplate x:Key="HeaderTemplate">
<TextBlock Text="{Binding Path=DisplayName}" />
</DataTemplate>
</UserControl.Resources>
<Grid>
<telerik:RadDocking>
<telerik:RadDocking.DocumentHost>
<DockPanel>
<telerik:RadTabControl ContentTemplate="{StaticResource ContentTemplate}"
ItemTemplate="{StaticResource HeaderTemplate}"
ItemsSource="{Binding TabCollection}"
SelectedItem="{Binding Path=SelectedItem,
Mode=OneWayToSource}" />
</DockPanel>
</telerik:RadDocking.DocumentHost>
</telerik:RadDocking>
</Grid>

Related

Binding parameter to a nested ListView using SelectedItem

I'm pretty new to coding in c# and I'm trying on a wpf app to transfer data between different folders.
To visualize the folders and subfolders I've got a tabcontrol with different mainfolders, and under each tab a ListView with information on the subfolders.
All data is gathered in a BindingList 'Klinieken', which is filled with objects 'Kliniek' (mainfolders) which contains objects 'Patient' (subfolders) which is filled with information about said folder. Here is the .xaml file for the mainwindow:
<Grid x:Name="LayoutRoot" Background="#555555">
<Grid.RowDefinitions>
<RowDefinition Height="420" />
<RowDefinition Height="116" />
</Grid.RowDefinitions>
<Grid x:Name="KliniekTabs" Background="#555555" Grid.Row="0" Margin="10 10 10 10">
<TabControl ItemsSource="{Binding Klinieken}"
SelectedItem="{Binding BronKliniek}"
TabStripPlacement="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding KliniekNaam}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding Path=Patienten}"
SelectedItem="{Binding Path=SelectedPatient}">
<ListView.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Patientnaam"
Width="250"
DisplayMemberBinding="{Binding PatientNaam}"/>
<GridViewColumn Header="Zisnr"
Width="250"
DisplayMemberBinding="{Binding PatientZis}"/>
<GridViewColumn Header="Aanmaakdatum"
Width="250"
DisplayMemberBinding="{Binding AanmaakDatum}"/>
</GridView>
</ListView.View>
</ListView>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
<Grid x:Name="WindowControls" Background="#777777" Grid.Row="1" Margin="100 5 100 10">
<StackPanel Orientation="Vertical">
<Label Margin="-80 0 0 0" HorizontalAlignment="Center">Doel kliniek</Label>
<ComboBox ItemsSource="{Binding Path=Klinieken}"
x:Name="doelKliniekDD"
SelectedItem="{Binding DoelKliniek}"
Margin="-120 0 0 0"
HorizontalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=KliniekNaam}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<StackPanel Orientation="Horizontal" Margin="20 0 0 0" Height="50" VerticalAlignment="Bottom" HorizontalAlignment="Center">
<Button Height="25" Style="{StaticResource Button_Orange}" Command="{Binding TransferButtonCommand}" CommandParameter="{Binding ElementName=window, Mode=OneWay}" Margin="0 0 10 0">Transfer</Button>
<Button Height="25" Style="{StaticResource Button_Orange}" Command="{Binding CopyButtonCommand}" CommandParameter="{Binding ElementName=window, Mode=OneWay}" Margin="10 0 20 0">Kopieer</Button>
<Button Height="25" Style="{StaticResource Button_Grey}" Command="{Binding CloseCommand}" CommandParameter="{Binding ElementName=window, Mode=OneWay}" Margin="20 0 0 0">Cancel</Button>
</StackPanel>
</StackPanel>
</Grid>
</Grid>
And here is the MainViewModel that is referenced in the .xaml:
//attributen
string monacoDirectory;
List<string> aanwezigeClinics;
Installation installation;
//constructor
public MainViewModel()
{
monacoDirectory = ConfigurationManager.AppSettings["monacoDirectory"];
aanwezigeClinics = new List<string>(Directory.GetDirectories(monacoDirectory, "*", SearchOption.TopDirectoryOnly));
aanwezigeClinics.Remove(monacoDirectory + #"\defaults");
aanwezigeClinics.Remove(monacoDirectory + #"\physics");
installation = new Installation(aanwezigeClinics);
Klinieken = new BindingList<Kliniek>();
foreach (var kliniek in installation.Klinieken)
{
Klinieken.Add(kliniek);
}
}
private Kliniek _doelKliniek;
private Kliniek _bronKliniek;
private Patient _selectedPatient;
private BindingList<Kliniek> _klinieken = new BindingList<Kliniek>();
public BindingList<Kliniek> Klinieken
{
get { return _klinieken; }
set { _klinieken = value; }
}
public Kliniek DoelKliniek
{
get { return _doelKliniek; }
set
{
_doelKliniek = value;
OnPropertyChanged();
}
}
public Kliniek BronKliniek
{
get { return _bronKliniek; }
set
{
_bronKliniek = value;
OnPropertyChanged();
}
}
public Patient SelectedPatient
{
get { return _selectedPatient; }
set
{
_selectedPatient = value;
MessageBox.Show("hoi");
OnPropertyChanged();
}
}
The data shows up in the gui just fine, which i was quite happy with. And using SelectedItem on the TabControl and the ComboBox i use later worked out beautifully aswell, but I just cant seem to get the SelectedItem on the ListView to work. I have added a button to test the output of the fields and SelectedPatient always returns as null.
If i check the 'Live Visual Tree' in VS and go to the properties of the ListView i can see the Patient as SelectedItem, so that tells me its not a selection but a binding problem. Furthermore ive tried to google for nested bindings, but the suggestions there didnt change anything about my situation.
Is a nested binding like this possible, or should i take a whole different approach?
Had a similar problem quite a while ago. The suggestions below solved it for me at the time.
Use SelectedValue instead of SelectedItem. This is because SelectedItem doesn't change until the control has been validated. SelectedValue changes whenever the user selects an item. The code you could use is as follows:
SelectedValue="{Binding BronKliniek, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
For the button part, try to provide CommandParameter="{Binding}" instead of CommandParameter="{Binding ElementName=window, Mode=OneWay}". This way, you'll get the value of the current DataContext of the button, which you can then use to extract the data you want from this DataContext.

Tabcontrol with different pages (included in xaml or extern page) as tabcontent

Hello and thanks for reading and maybe helping :-)
My Code below with statements.
I have my MainWindow.xaml in which I have my CheckBox and TabControl.
<CheckBox Grid.Column="8" Grid.Row="2" Name="checkBoxCommon" HorizontalAlignment="Right" VerticalAlignment="Center" IsChecked="{Binding IsCheckedCommon}"/>
<Grid Grid.Column="0" Grid.ColumnSpan="10" Grid.Row="4">
<TabControl ItemsSource="{Binding Path=DpConfigCol>
<TabControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding Path=DpConfigName}>
<ContentControl.Resources>
<DataTemplate DataType="types:ConfigCommon">
</DataTemplate>
<DataTemplate DataType="types:ConfigAdress">
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Resources>
<DataTemplate DataType="types:ConfigCommon">
<TextBlock Text="hallo" Width="150"/>
</DataTemplate>
<DataTemplate DataType="types:ConfigAdress">
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
If I tick my checkbox it will add a new model to my ObservableCollection().
private bool _isCheckedCommon;
public bool IsCheckedCommon
{
get { return _isCheckedCommon; }
set
{
_isCheckedCommon = value;
if (_isCheckedCommon == true)
{
DpConfigCol.Add(new ConfigCommon("Common"));
}
else
{
foreach (object item in DpConfigCol)
{
if (item.GetType().ToString()==typeof(ConfigCommon).FullName.ToString())
{
DpConfigCol.Remove(item);
break;
}
}
}
return;
}
}
I made a binding to my tabcontrol with this Collection called DpConfigCol.
And now my question:
How do I make the properties (from my model: ConfigCommon) visible in my tabcontrol.ContentTemplate? If there are more than one checkbox with even more Models?
Is there a way to implement in that Tabcontrol.contentTemplate more than one Template for more than one model?
TLDR; (read only the title)
I also had to develop an app that used tabs once. You can use <Frame> inside your tabs and set its Content to what ever XAML page you want

Show separators for WindowCommands when using databound ItemsControl in MahApps.Metro

When manually adding buttons as window commands, the separators show up normally:
<controls:MetroWindow.RightWindowCommands>
<controls:WindowCommands ShowSeparators="True">
<Button Content="Button1" />
<Button Content="Button2" />
</controls:WindowCommands>
</controls:MetroWindow.RightWindowCommands>
When trying to dynamically create buttons as window commands in XAML using an ItemsControl, I can't get the separators to show up correctly. The buttons themselves look correct aside from the margin/padding which will likely be fixed when the separators are fixed.
Here's my XAML:
<controls:MetroWindow.RightWindowCommands>
<controls:WindowCommands ShowSeparators="True">
<ItemsControl ItemsSource="{Binding GamesViewModel.Games}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type entities:Game}">
<Button Content="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</controls:WindowCommands>
</controls:MetroWindow.RightWindowCommands>
Can anyone spot what I'm doing wrong? I thought it might have something to do with the ItemsPanel I'm setting, but I had to do that to make the buttons horizontal. Perhaps there's a style/template I'm overriding?
This code-behind version does work, but I'd rather do it all through XAML:
foreach (Game game in ((MainViewModel)DataContext).GamesViewModel.Games)
WindowCommands.Items.Add(new Button { Content = game.Name });
I think you've already noticed the problem without realising it! WindowCommands is an ItemsControl itself so just give it an ItemsSource and it'll be sorted.
The key thing is to feed it WindowCommandsItem instead of your viewmodel (this really feels like a bug in 0.13 alpha series where i tested as it used to work without this special conversion in past if memory serves.)
Here's bits of code that I tested it with:
XAML Bits:
<controls:MetroWindow.DataContext>
<wpfApplication1:ViewModel />
</controls:MetroWindow.DataContext>
<controls:MetroWindow.Resources>
<DataTemplate DataType="{x:Type wpfApplication1:Model}">
<Button Content="{Binding Name}" />
</DataTemplate>
<wpfApplication1:ModelToWindowCommandsItemConverter x:Key="Converter" />
</controls:MetroWindow.Resources>
<controls:MetroWindow.RightWindowCommands>
<controls:WindowCommands ItemsSource="{Binding Commands, Converter={StaticResource Converter}}" />
</controls:MetroWindow.RightWindowCommands>
Converter code used:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ return ((IEnumerable<Model>)value).Select(x => new WindowCommandsItem { Content = x }); }
ViewModel:
public class ViewModel
{
public ObservableCollection<Model> Commands { get; } = new ObservableCollection<Model>
{
new Model { Name = "Skyrim" },
new Model { Name = "Fallout 4" },
new Model { Name = "Fallout NV" }
};
}
Model:
public class Model
{
public string Name { get; set; }
}
Result:
In connection with #Maverik answer, there is no need to use a converter for it. Just put the DataTemplate on WindowCommands.ItemTemplate.
<controls:MetroWindow.RightWindowCommands>
<controls:WindowCommands ItemsSource="{Binding Commands}">
<controls:WindowCommands.ItemTemplate>
<DataTemplate DataType="{x:Type local:Model}">
<Button Content="{Binding Name}" />
</DataTemplate>
</controls:WindowCommands.ItemTemplate>
</controls:WindowCommands>
</controls:MetroWindow.RightWindowCommands>

wpf check list box

I m new to wpf.In order to get check list box functionality ,I have added below xaml to my code,but there is no output in my screen.only blank,what it could be?
<TabItem Header="Samples" >
<ListBox Margin="10" Width="373" Height="236">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MyText"/>
<CheckBox IsChecked="False"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</TabItem>
just have a look at this basic sample
http://merill.net/2009/10/wpf-checked-listbox/
List box is a bit wired for such task..Have a look at ItemsControl.
Here is the code i use:
<ItemsControl
ItemsSource="{Binding ***}" IsTabStop="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox
Content="{Binding Name}"
IsChecked="{Binding IsSelected}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Replace your code with this
<TabItem Header="Roles" >
<ListBox Margin="10" Width="373" Height="236">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MyText"/>
<CheckBox IsChecked="False"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBoxItem>Hi</ListBoxItem>
</ListBox>
</TabItem>
and tell us if it still shows blank
Better still, just use the new CheckListBox control in the Extended WPF Toolkit
http://wpftoolkit.codeplex.com/wikipage?title=CheckListBox&referringTitle=Home
This might help
1.Inorder to work datatemplate you must specify itemsource, here i have bounded a Stateslist a collection of items into it.
2.Also set the Datacontext to ViewModel or the CodeBehind as datacontext.
3.Datacontext will distribute the StateList properties collection to the listbox itemsource
using codebehind -
public Window1()
{
InitializeComponent();
this.DataContext = this;
LoadData();
}
using viewmodel
public Window1()
{
InitializeComponent();
DataContext = new Window1ViewModel();
LoadData();
}
//MyItemsource Property for listbox
private ObservableCollection<States> _stateslist;
public ObservableCollection<States> StatesList
{
get { return _stateslist; }
set
{
_stateslist = value;
RaisePropertyChanged(() => StatesList);
}
}
// Sample Data Loading
public void LoadData()
{
StatesList = new ObservableCollection<States>();
StatesList.Add(new States
{
StateName = "Kerala"
});
StatesList.Add(new States
{
StateName = "Karnataka"
});
StatesList.Add(new States
{
StateName = "Goa"
});
}
Window1.Xaml
<ListBox ItemsSource="{Binding StatesList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding IsSelected"} Content="{Binding StateName}" />
<TextBox Text="{Binding TextBoxValue}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Check this out it isworking..you are using TabItem but you didnt define it in TabControl
<TabControl>
<TabItem Header="Tab1">
<ListBox Margin="10" Width="373" Height="236">
<ListBox.Items>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MyText"/>
<CheckBox IsChecked="False"/>
</StackPanel>
</ListBox.Items>
</ListBox>
</TabItem>
</TabControl>
If you are new in WPF use XamlPadX it will give you great help to practice out on it..

How to access base datacontext in Collection in xaml

I have a wpf app with the datacontext set to an instance of a viewmodel class. It all works fine except where I need to access a property of the viewmodel in a listbox with the datacontext set to a collection that is contained in the ViewModel class. On msdn it says you can escape using the \ character but that has not worked for me
My code
public class StatusBoardViewModel : INotifyPropertyChanged
{
OIConsoleDataContext db = new OIConsoleDataContext();
// the collection
private IQueryable<Issue> issues;
public IQueryable<Issue> Issues
{
get
{
// Lazy load issues if they have not been instantiated yet
if (issues == null)
QueryIssues(); // This just runs a linq query to set the property
return issues;
}
set
{
if (issues != value)
{
issues = value;
OnPropertyChanged("Issues");
}
}
}
// The property I need to access
private bool showDetailListItems = true;
public bool ShowDetailListItems
{
get
{
return showDetailListItems;
}
set
{
if (showDetailListItems != value)
{
showDetailListItems = value;
OnPropertyChanged("ShowDetailListItems");
}
}
}
}
in the window1.xaml.cs
//instantiate the view model
StatusBoardViewModel statusBoardViewModel = new StatusBoardViewModel();
public Window1()
{
InitializeComponent();
// setting the datacontext
this.DataContext = statusBoardViewModel;
}
And the Xaml
// This is in the Window1.xaml file
<ListBox x:Name="IssueListBox"
ItemsSource="{Binding Issues}" // Binds the listbox to the collection in the ViewModel
ItemTemplate="{StaticResource ShowIssueDetail}"
IsSynchronizedWithCurrentItem="True"
HorizontalContentAlignment="Stretch" BorderThickness="3"
DockPanel.Dock="Top" VerticalContentAlignment="Stretch"
Margin="2" MinHeight="50" />
// The datatemplate from the app.xaml file
<DataTemplate x:Key="ShowIssueDetail">
<Border CornerRadius="3" Margin="2" MinWidth="400" BorderThickness="2"
BorderBrush="{Binding Path=IssUrgency, Converter={StaticResource IntToRYGBBoarderBrushConverter}}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=IssSubject}" Margin="3" FontWeight="Bold" FontSize="14"/>
<!--DataTrigger will collapse following panel for simple view-->
<StackPanel Name="IssueDetailPanel" Visibility="Visible" Margin="3">
<StackPanel Width="Auto" Orientation="Horizontal">
<TextBlock Text="Due: " FontWeight="Bold"/>
<TextBlock Text="{Binding Path=IssDueDate}" FontStyle="Italic" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Width="Auto" Orientation="Horizontal">
<TextBlock Text="Category: " FontWeight="Bold"/>
<TextBlock Text="{Binding Path=IssCategory}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
// This is where I have the issue, ShowDetailListItems is in the base class, not the collection
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=ShowDetailListItems, Mode=TwoWay}" Value="False">
<Setter TargetName="IssueDetailPanel" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I have learned a TON doing this but this current issue is driving me batty, no luck with google, MSDN, SO or a couple books
I thought I would add this note: I am learning this in order to build some apps for my business, I am a rank beginner in wpf and xaml so I relize this is probably somthing silly. I would really like to find a good tutorial on datacontexts as what I do find is a dozen different "How To's" that are all totally different. I know I have some big holes in my knowledge because I keep ending up with multiple instantiations of my viewmodel class when I try to create references to my datacontext in the codebehind, Window1.xaml and app.xaml files.
Have you tried one of these?
{Binding Path=ShowDetailListItems, ElementName=YourWindowName}
or
{Binding ShowDetailListItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}

Categories