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}}}
Related
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.
I am trying to create view using Telerik for WPF. The View should present collection loaded in VM after button click. Every element from collection is inheriting from base class:
public class BindableProfileData : BindableBase
{
private ProfileItem _profileModel;
private BindableDPDataItem _dataModel;
public int Row
{
get { return _profileModel.DrawingSettings.Row; }
}
public int Column
{
get
{
return _profileModel.DrawingSettings.Column;
}
}
public int RowSpan
{
get { return _profileModel.DrawingSettings.RowSpan; }
}
public int ColumnSpan
{
get { return _profileModel.DrawingSettings.ColumnSpan; }
}
}
so for example in collection may appear object of
BarContainer : BindableProfileData {}
Now to present content i created that view:
<Grid>
<FrameworkElement.Resources>
<DataTemplate x:Key="ItemTemplate" >
<TextBlock Text="{Binding Name}" />
</DataTemplate>
<DataTemplate x:Key="ContentTemplate">
<StackPanel>
<TextBlock Text="{Binding Row, StringFormat='Row: {0}'}"/>
<TextBlock Text="{Binding Column, StringFormat='Column: {0}'}"/>
<TextBlock Text="{Binding RowSpan, StringFormat='RowSpan: {0}'}"/>
<TextBlock Text="{Binding ColumnSpan, StringFormat='ColumnSpan: {0}'}"/>
</StackPanel>
</DataTemplate>
</FrameworkElement.Resources>
<Grid>
<telerik:RadTileView x:Name="xTileView"
ItemTemplate="{StaticResource ItemTemplate}"
ContentTemplate="{StaticResource ContentTemplate}"
ItemsSource="{Binding SelectedCategory}"
IsAutoScrollingEnabled="True"
ColumnWidth="500"
RowHeight="300">
<telerik:RadTileView.ItemsPanel>
<ItemsPanelTemplate>
<controlls:MultipleRowsAndColumnsPanel RowsCount="4" ColumnsCount="3"/>
</ItemsPanelTemplate>
</telerik:RadTileView.ItemsPanel>
<telerik:RadTileView.ItemContainerStyle>
<Style TargetType="telerik:RadTileViewItem">
<Setter Property="controlls:TileViewAttachedProperties.Row" Value="{Binding Row}"/>
<Setter Property="controlls:TileViewAttachedProperties.Column" Value="{Binding Column}"/>
<Setter Property="controlls:TileViewAttachedProperties.RowSpan" Value="{Binding RowSpan}"/>
<Setter Property="controlls:TileViewAttachedProperties.ColumnSpan" Value="{Binding ColumnSpan}"/>
</Style>
</telerik:RadTileView.ItemContainerStyle>
</telerik:RadTileView>
</Grid>
</Grid>
Which uses helpers from telerik guides (MultipleRowsAndColumnsPanel, TileViewAttachedProperties)
I have two problems. First why view not appears after Collection change.
When I cut:`
<telerik:RadTileView.ItemsPanel>
<ItemsPanelTemplate>
<controlls:MultipleRowsAndColumnsPanel RowsCount="4" ColumnsCount="3"/>
</ItemsPanelTemplate>
</telerik:RadTileView.ItemsPanel>
`
from code and paste it again (Visual Studio edit and run) view appears just How I want it to be. So it looks like ItemsPanel not reacting on collection Change, but how to fix it?
And the second question how can I specify different templates for different classes in collection. I found similar post but I don't know how to do it with RadViewTableItem because it has two elements (ItemTemplate and ContentTemplate) usually specified in RadTileView.
Any advice whould be very helpfull for me. Thank you :)
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>
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
i have some code in wpf in that i have used busyindicator and i set datatemplete now my problem is that i used mvvm pattern in my applicaton and i want to used busyindicator on that but i don't know how to binding textblock inside busyindicaor datatemplete.my code look like
<extended:BusyIndicator Name="_busyIndicator">
<extended:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="Downloading Email" FontWeight="Bold" HorizontalAlignment="Center" Name="Dhaval"/>
<StackPanel Margin="4">
<TextBlock Text="Downloading message 4/10..."/>
<ProgressBar Value="40" Height="15" x:Name="Progress_Dhaval"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</extended:BusyIndicator.BusyContentTemplate>
You can use Binding with RelativeSource.
Add in your ViewModel this property:
private string _busyText;
public string BusyText
{
get { return _busyText; }
set { _busyText = value; RaisePropertyChanged(() => BusyText); }
}
And change this line:
<TextBlock Text="Downloading message 4/10..."/>
on this one:
<TextBlock Text="{Binding Path=DataContext.BusyText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />