What I'm trying to do is bind the controls generated by a ItemsControl.ItemTemplate to a new instance of my ContactInterface class that is created whenever I run my query function.
ContactInterface.cs:
class ContactInterface : INotifyPropertyChanged
{
public string Type { get; set; }
private string firstname;
public string FirstName {
get{return this.firstname;}
set {
if (this.firstname != value) {
this.firstname = value;
this.NotifyPropertyChanged("FirstName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
SQL.cs
partial class MainWindow
{
private void selectContact(int? contactID)
{
using (ContactsDataContext context = new ContactsDataContext("Data Source=ds;Initial Catalog=db;Integrated Security=True"))
{
Contact contact = context.Contacts.SingleOrDefault(x => x.ContactID == contactID);
Contact spouse = context.Contacts.SingleOrDefault(x => x.ContactID == contact.Spouse);
Property property = context.Properties.SingleOrDefault(x => x.PropertyID == contact.Address);
ContactInterface selectedContact= new ContactInterface();
selectedContact.FirstName = contact.FirstName;
profileGrid.DataContext = selectedContact;
}
}
MainWindow.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ItemsControl Name="Profile_Page_Controls">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Margin="15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="43" />
<RowDefinition Height="43" />
<RowDefinition Height="43" />
<RowDefinition Height="43" />
<RowDefinition Height="43" />
<RowDefinition Height="43" />
<RowDefinition Height="43" />
<RowDefinition Height="43" />
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding TextContent}"/>
<TextBox Height="23" Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column" Value="{Binding profile_Column}" />
<Setter Property ="Grid.Row" Value="{Binding profile_Row}" />
<Setter Property="Grid.ColumnSpan" Value="{Binding profile_Colspan}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
<CheckBox Name="profile_SpouseCheck" Grid.Column="1" Margin="0,0,0,0" Checked="profile_SpouseCheck_Checked_1" Unchecked="profile_SpouseCheck_Checked_1"> Spouse</CheckBox>
</Grid>
My plan is to make a new ContactInterface instance every time a user runs the selectContact() function. That new instance will be bound to the generated controls, so when the user makes and saves a change, the textboxes will update that ContactInterface instance and my other functions can grab the data from that ContactInterface to make changes to the database.
Any ideas?
Related
I am new to c# and wpf.
Im my project I am trying to create a new tabs/ remove tab dynamically using button click.
But I am not successfull.
Kindly suggest where I am wrong.
I have taken help from here
https://www.technical-recipes.com/2017/adding-tab-items-dynamically-in-wpf/
I did DataBinding using command for Add/Remove of tab.
I have other .cs files similar as mentioned above. Hence not copy-pasting them here.
Below is my code:
MainWindow.xaml
<Window x:Class="UI_1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UI_1"
mc:Ignorable="d"
Title="MainWindow" Height="650" Width="758">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="20.8"/>
<RowDefinition Height="Auto" MinHeight="118.4"/>
<RowDefinition MinHeight="118.4" Height="228"/>
<RowDefinition />
<RowDefinition Height="13*" />
</Grid.RowDefinitions>
<Menu Grid.Row="0" Margin="0,0,-0.4,118.2" Grid.RowSpan="2" >
<MenuItem Header="File">
<MenuItem Header="Open Log File" />
<MenuItem Header="Open Workspace" />
<Separator />
<MenuItem Header="Save as Workspace" />
<MenuItem Header="Set Path host file" />
</MenuItem>
<MenuItem Header="Control">
<MenuItem Header="Open Command Line View" />
</MenuItem>
</Menu>
<WrapPanel Grid.Row="1" Grid.RowSpan="2" Margin="0,0.2,-0.4,0.2" >
<WrapPanel.Resources>
<Style TargetType="Grid">
<Setter Property="Margin" Value="3"/>
</Style>
</WrapPanel.Resources>
<Border BorderBrush="Black" BorderThickness="1" Grid.Column="0">
<Grid Width="180">
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="1"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="3"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="3"/>
</Style>
</Grid.Resources>
<TextBlock Grid.Column="0" Grid.ColumnSpan="4" VerticalAlignment="Center" Text="Connect To" Margin="3,6,2.8,6" />
<TextBlock Grid.Row="1" Grid.ColumnSpan="4" VerticalAlignment="Center" Text="Log Cmd" Margin="3,8,2.8,8" />
<TextBox Grid.Column="3" Grid.ColumnSpan="3" Text="IP/HostName" Margin="32,3,3.2,3"/>
<ComboBox
x:Name="Job" Grid.Row = "1" Grid.Column="3" Grid.ColumnSpan="3" Margin="32,3,42.2,6">
<ComboBoxItem Content="Trace"/>
<ComboBoxItem Content="List"/>
<ComboBoxItem Content="Dump"/>
<ComboBoxItem Content="Off"/>
<ComboBoxItem Content="On"/>
</ComboBox>
<TextBox Grid.Column="5" Grid.Row = "1" Text="Line" Margin="6.4,3,2.2,6"/>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="6*"/>
<ColumnDefinition Width="27*"/>
<ColumnDefinition Width="45*" />
<ColumnDefinition Width="45*" />
<ColumnDefinition Width="46*" />
</Grid.ColumnDefinitions>
</Grid>
</Border>
<GridSplitter Width="2"></GridSplitter>
<Border BorderBrush="Black" BorderThickness="1" >
<Grid Grid.Column="1" Width="120">
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="2"/>
</Style>
</Grid.Resources>
<Button Content="Play" Margin="2,5,2.2,2"
Command="{Binding x.AddItem}"/>
<Button Content="Stop" Grid.Column="1" Margin="2,5,2.2,2"
Command="{Binding x.RemoveTab}"/>
<Button Content="Send" Grid.ColumnSpan="2" Grid.Row="1" />
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</Border>
<Border BorderBrush="Black" BorderThickness="1" Height="456" Grid.Row="3" >
<TabControl x:Name="tabControl1" HorizontalAlignment="Stretch" MinHeight="50" Margin="0,0.2" Width="736"
ItemsSource="{Binding Titles, Mode=TwoWay}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Border>
</WrapPanel>
</Grid>
</Window>
MainWindow.XAML.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace UI_1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
class MainWindowVM : INotifyPropertyChanged
{
public class Item
{
public string Header { get; set; }
public string Content { get; set; }
}
public ObservableCollection<Item> Titles
{
get { return _titles; }
set
{
_titles = value;
OnPropertyChanged("Titles");
}
}
static int tabs = 1;
private ObservableCollection<Item> _titles;
private ICommand _addTab;
private ICommand _removeTab;
public ICommand AddTab
{
get
{
return _addTab ?? (_addTab = new TabRelayCommand(
x =>
{
AddTabItem();
}));
}
}
public ICommand RemoveTab
{
get
{
return _removeTab ?? (_removeTab = new TabRelayCommand(
x =>
{
RemoveTabItem();
}));
}
}
private void RemoveTabItem()
{
Titles.Remove(Titles.Last());
tabs--;
}
private void AddTabItem()
{
var header = "Tab " + tabs;
var content = "Content " + tabs;
var item = new Item { Header = header, Content = content };
Titles.Add(item);
tabs++;
OnPropertyChanged("Titles");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public partial class MainWindow : Window
{
public MainWindowVM x;
public MainWindow()
{
InitializeComponent();
x = new MainWindowVM();
this.DataContext = x;
}
}
After clicking my Play button no new tab is adding
I have a command in my viewmodel that, when executed, will add an item to an observablecollection. In my view, an ItemsControl has its source bound to this collection. So whenever an item is added, a combobox and a textblock will appear for each item.
What I'm trying to do is, have each set of combobox/textblock appear to the right of the last set, until it reaches the end of window, and then the next set will appear below the first, and so on. Was planning to put this in a ScrollViewer. Everything I've tried so far keeps placing each set under the last one, instead of to the right.
Here's my simple model class:
public class DataModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private double _gridColumn;
public double GridColumn
{
get { return _gridColumn; }
set { _gridColumn = value; OnPropertyChanged("GridColumn"); }
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Here's the relevant parts of the viewmodel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private int columnNum = 0;
private ObservableCollection<DataModel> _dataModelCollection = new ObservableCollection<DataModel>();
public ObservableCollection<DataModel> DataModelCollection
{
get { return _dataModelCollection; }
set { _dataModelCollection = value; OnPropertyChanged("DataModelCollection"); }
}
public void AddDataModel(object parameter)
{
DataModelCollection.Add(new DataModel {GridColumn = columnNum % 8 });
columnNum += 2;
}
}
And now my view:
<Grid Background="#44D3D3D3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140*" />
<ColumnDefinition Width="7*" />
<ColumnDefinition Width="140*" />
<ColumnDefinition Width="7*" />
<ColumnDefinition Width="140*" />
<ColumnDefinition Width="7*" />
<ColumnDefinition Width="140*" />
<ColumnDefinition Width="7*" />
<ColumnDefinition Width="140*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="46*" />
<RowDefinition Height="139*" />
<RowDefinition Height="38*" />
<RowDefinition Height="78*" />
<RowDefinition Height="60*" />
<RowDefinition Height="50*" />
</Grid.RowDefinitions>
<Button Grid.Row="3"
Grid.Column="2"
Content="Add Set"
Command="{Binding AddDataModelCmd}"/>
<ItemsControl Grid.Row="4"
Grid.RowSpan="2"
Grid.ColumnSpan="9"
ItemsSource="{Binding DataModelCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Grid.Column="{Binding GridColumn}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140*" />
<ColumnDefinition Width="7*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="7" />
<RowDefinition Height="53" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<ComboBox Grid.Column="0" Grid.Row="1" />
<TextBlock Text="tester"
Grid.Column="0"
Grid.Row="2"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I've messed around with setting the ItemsPanelTemplate as a WrapPanel which gave me the side by side result, but I couldn't get any spacing between each set of items.
I am trying to make a custom user control that I can reuse across all of my views. My BaseViewModel has a property called ViewAlerts that is intended to be used to show alerts consistently across the application (such as successful updates, failed requests, etc.). I was able to get to the point where my custom control is buildable and has a property for binding, but the collection of alerts is never show. I am statically defining some alerts in my base view model for testing purposes, and am not able to get the alerts to show (seems like a problem with an INotifyPropertyChanged but my bindable property inherits from ObservableCollection<> so it should be automatically handling that I think).
Here is my custom control so far:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Resources/MMJCeoResources.xaml" />
<ResourceDictionary>
<Style TargetType="TextBlock" x:Key="AlertTextStyle">
<Setter Property="FontSize" Value="15"></Setter>
<Setter Property="Margin" Value="10" />
<Setter Property="Padding" Value="10" />
</Style>
<DataTemplate x:Key="DangerAlert">
<Grid Background="LightPink">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Border Width="10"
Height="10"
BorderBrush="DarkRed"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0">
<TextBlock Text="X" />
</Border>
<TextBlock Grid.Row="1" Text="{Binding Message}" Style="{StaticResource AlertTextStyle}" Foreground="DarkRed"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="SuccessAlert">
<Grid Background="LightGreen">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Border Width="10"
Height="10"
BorderBrush="DarkGreen"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0">
<TextBlock Text="X" />
</Border>
<TextBlock Grid.Row="1" Text="{Binding Message}" Style="{StaticResource AlertTextStyle}" Foreground="DarkGreen"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="InfoAlert">
<Grid Background="LightGreen">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Border Width="10"
Height="10"
BorderBrush="DarkGreen"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0">
<TextBlock Text="X" />
</Border>
<TextBlock Grid.Row="1" Text="{Binding Message}" Style="{StaticResource AlertTextStyle}" Foreground="DarkGreen"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="WarningAlert">
<Grid Background="LightSalmon">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Border Width="10"
Height="10"
BorderBrush="DarkOrange"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0">
<TextBlock Text="X" />
</Border>
<TextBlock Grid.Row="1" Text="{Binding Message}" Style="{StaticResource AlertTextStyle}" Foreground="DarkOrange"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Path=Alerts}"
ItemTemplateSelector="{StaticResource AlertDataTemplateSelector}">
</ItemsControl>
</Grid>
</UserControl>
The Code Behind for the control:
public sealed partial class AlertControl : UserControl
{
public static readonly DependencyProperty AlertsProperty = DependencyProperty.Register(
"Alerts", typeof (ObservableList<Alert>), typeof (AlertControl), new PropertyMetadata(default(ObservableList<Alert>), OnAlertsChanged));
public ObservableList<Alert> Alerts
{
get { return (ObservableList<Alert>) GetValue(AlertsProperty); }
set { SetValue(AlertsProperty, value); }
}
//I was trying to adapt another tutorial to what I was trying to accomplish but only got this far
public static void OnAlertsChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var old = e.OldValue as ObservableList<Alert>;
var me = sender as AlertControl;
if (old != null)
{
old.CollectionChanged -= me.OnAlertCollectionChanged;
}
var n = e.NewValue as ObservableList<Alert>;
if (n != null)
n.CollectionChanged += me.OnAlertCollectionChanged;
}
private void OnAlertCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
Alerts.Clear();
var n = e.NewItems as ObservableList<Alert>;
Alerts.AddRange(n);
}
}
public AlertControl()
{
this.InitializeComponent();
DataContext = this;
}
}
an example implementation:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource PageTitle}" Text="User Information" />
<controls:AlertControl Alerts="{Binding ViewAlerts}" />
in this implementation, the ViewAlerts property has 4 statically defined alerts in it, so I know there are values that should be showing up.
You should give your inner Grid the DataContext to this and not to the Control itself, because then the outer binding will search for your ViewAlerts inside the Control
<Grid x:Name="InnerGrid">
<ItemsControl ItemsSource="{Binding Path=Alerts}"
ItemTemplateSelector="{StaticResource AlertDataTemplateSelector}">
</ItemsControl>
</Grid>
public AlertControl()
{
this.InitializeComponent();
InnerGrid.DataContext = this;
}
After that you can Bind to Alerts, and Alerts will be binded to the ItemsControl inside your InnerGrid
I'm a beginner at WPF, and I'm trying to bind a nested collection.
I've found many topics online about binding, and I tried following this question/answer. I tried changing the Datacontext and ItemSource values, but I just can't seem to get it right.
XAML:
<UserControl x:Class="WpfApplication1.Nav1"
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"
mc:Ignorable="d"
d:DesignHeight="620" d:DesignWidth="950">
<Grid Background="#FF741125">
<TextBlock Height="61" HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBlock1" Text="BEVERAGES" VerticalAlignment="Top" Width="250" FontSize="20" FontWeight="Black" />
<ItemsControl x:Name="Stack" DataContext="{Binding myMenu}" ItemsSource="{Binding Subs}" BorderThickness="0" Margin="0,60,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Name}" FontWeight="Bold" />
<ItemsControl ItemsSource="{Binding Nodes}" BorderThickness="0" Margin="0,60,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Height="60" Width="900">
<Grid.Background>
<ImageBrush ImageSource="/WpfApplication1;component/Images/MenuGrid.fw.png" />
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="10" />
<RowDefinition Height="10" />
<RowDefinition Height="10" />
<RowDefinition Height="10" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Name}" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3" Grid.ColumnSpan="14" FontWeight="Bold" />
<Label Content="{Binding Description}" Grid.Column="1" Grid.Row="2" Grid.RowSpan="3" Grid.ColumnSpan="14" />
<Label Content="{Binding Cost}" ContentStringFormat="{}${0}" Grid.Column="16" Grid.Row="0" Grid.RowSpan="5" Grid.ColumnSpan="2" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Class:
public class Menu
{
public ObservableCollection<Category> Subs;
public Menu()
{
Subs = new ObservableCollection<Category>();
}
}
public class Category
{
public ObservableCollection<MenuItem> Nodes;
public Category()
{
Nodes = new ObservableCollection<MenuItem>();
}
private string name;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public Category(string name)
{
this.name = name;
Nodes = new ObservableCollection<MenuItem>();
}
}
public class MenuItem
{
public MenuItem()
{
}
public MenuItem(string name, string description, double cost)
{
this.itemName = name;
this.description = description;
this.cost = cost;
}
private string itemName;
public string ItemName
{
get { return this.itemName; }
set { this.itemName = value; }
}
private string description;
public string Description
{
get { return this.description; }
set { this.description = value; }
}
private double cost;
public double Cost
{
get { return this.cost; }
set { this.cost = value; }
}
}
CS:
public Nav1()
{
InitializeComponent();
// Init Model
Menu myMenu = new Menu();
myMenu.Subs.Add(new Category("Soft Drinks"));
myMenu.Subs.Add(new Category("Coffee"));
myMenu.Subs.Add(new Category("Premium"));
myMenu.Subs[0].Nodes.Add(new MenuItem("Pepsi", "Cool & Refreshing", 1.39));
myMenu.Subs[0].Nodes.Add(new MenuItem("Diet Pepsi", "Cool & Refreshing", 1.39));
myMenu.Subs[0].Nodes.Add(new MenuItem("7Up", "Cool & Refreshing", 1.39));
myMenu.Subs[0].Nodes.Add(new MenuItem("Mug Root Beer", "Cool & Refreshing", 1.39));
myMenu.Subs[0].Nodes.Add(new MenuItem("Brisk Iced Tea", "Cool & Refreshing", 1.39));
myMenu.Subs[0].Nodes.Add(new MenuItem("Bottled Water", "Thirsty? Aquafina.", 2.75));
// Set DataContext for StackPanel
Stack.DataContext = myMenu.Subs;
}
It displays the category names (eg. "Soft Drinks" or "Coffee") but I can't display the individual items. Also, are there better ways to do this? As mentioned, I'm a beginner at WPF and there seem to be many interesting functions I'm missing.
Thanks for reading!
Using a DataTemplate does not allow for the using of nested items since it does not provide an ItemsSource property.
<DataTemplate>
<TextBlock Text={Binding MyTextValue}/>
</DataTemplate>
However, if you use a HierarchicalDataTemplate, you can use the ItemsSource property to define the next level of items.
<HierarchicalDataTemplate ItemsSource={Binding MyListOfLevelTwoItems}>
<TextBlock Text={Binding MyLevelOneValue}/>
</HierarchicalDataTemplate>
<DataTemplate DataType={x:Type MyLevelTwoItem}>
<TextBlock Text={Binding MyLevelTwoValue}/>
</DataTemplate>
I am using Caliburn.Micro to do convention based binding of the contents of my listbox from a BindableCollection in my ViewModel.
I want to perform Validation on it so that when there is no selection and the user exits the form they get that nice red line around the listbox with the error message that they have to select a value. So far Caliburn has worked great for textbox validation but I can't seem to get it to work on the listbox!
On a side note, is there any way to forcibly run a complete validation from the VM? Currently I am merely doing manual validation by checking the values and changing them so it triggers the Validator which is a bit backwards..
Here is the code for my view:
<UserControl x:Class="AddSessionDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<toolkit:BusyIndicator IsBusy="{Binding IsBusy}" BusyContent="{Binding IsBusyStatusDisplay}">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="auto" />
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="5,9" />
</Style>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="5" />
</Style>
</Grid.Resources>
<TextBlock Text="Session Name" />
<TextBox x:Name="SessionName" Grid.Column="1" />
<TextBlock Text="Scenario" Grid.Row="1" />
<ListBox x:Name="Scenarios" Grid.Row="1" Grid.Column="1" DisplayMemberPath="Name" Margin="5" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Column="1" Grid.Row="3" Margin="5">
<Button x:Name="Cancel" Height="28" Margin="5" Content="Cancel" Padding="0,2" Width="75" />
<Button x:Name="OK" Height="28" Margin="5" Content="OK" Padding="0,2" Width="100" />
</StackPanel>
</Grid>
</toolkit:BusyIndicator>
Here is the (relevant) code for my viewmodel:
public BindableCollection<Scenario> Scenarios { get; set; }
[Required(ErrorMessage = "You must select a scenario")]
public Scenario SelectedScenario { get; set; }
private string _sessionName;
[Required(ErrorMessage="Session Name is invalid")]
public string SessionName
{
get { return _sessionName; }
set { _sessionName = value; NotifyOfPropertyChange(() => SessionName); }
}
public IEnumerable<IResult> OK()
{
if (SelectedScenario == null)
{
// This forces validation check, but doesnt show validation error on the view!
SelectedScenario = new Scenario();
SelectedScenario = null;
}
if (SessionName == null)
SessionName = "";
if (Validator.TryValidateObject(this, new ValidationContext(this), new List<ValidationResult>()))
{
IsBusy = true;
IsBusyStatusDisplay = "Creating Session...";
yield return new CreateSessionResult(SelectedScenario.Id, SessionName);
IsBusy = false;
yield return Close();
}
}