I was working on creating a reusable custom control (not a user control) in WPF. I created the XAML in the Themes/Generic.xaml file and I setup the "lookless" functionality in the control's CS file:
AddressField.cs
using ...;
namespace MyNamespace
{
[TemplatePart(Name = AddressField.ElementAddress1TextBox, Type = typeof(TextBox))]
public class AddressField : Control
{
private const String ElementAddress1TextBox = "PART_Address1TextBox";
public AddressEntity Address
{
get { return (AddressEntity)GetValue(AddressProperty); }
set { SetValue(AddressProperty, value); }
}
public static readonly DependencyProperty AddressProperty =
DependencyProperty.Register("Address", typeof(AddressEntity), typeof(AddressField), new UIPropertyMetadata(null, new PropertyChangedCallback(OnAddressPropertyChangedCallback)));
static AddressField()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AddressField), new FrameworkPropertyMetadata(typeof(AddressField)));
}
}
}
Themes\Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
>
<Style TargetType="{x:Type local:AddressField}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AddressField}">
<StackPanel>
<Grid x:Name="StandardView">
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Height" Value="17" />
<Setter Property="Margin" Value="3,3,3,3" />
</Style>
<Style TargetType="TextBox">
<Setter Property="Height" Value="23" />
<Setter Property="Margin" Value="3,3,3,3" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Address 1, Address2-->
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Text="Address:" TextWrapping="NoWrap" />
<TextBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="8" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinWidth="300"
x:Name="PART_Address1TextBox" Text="{Binding Path=Address1}" />
</Grid>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Form.xaml
<Window x:Class="NIC.BackOffice.Casino.RegistrationEntryForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:nic="clr-namespace:MyNamespace;assembly=MyStuff">
<Canvas>
<nic:AddressField Canvas.Left="10" Canvas.Top="10"
x:Name="OfficeAddressField" Address={Binding Path=OAddress} />
</Canvas>
</Window>
Form.cs
using ...;
namespace MyNamespace
{
public partial class Form : Window
{
public Form()
{
InitializeComponent();
OAddress = new AddressEntity("1234 Testing Lane");
}
public AddressEntity OAddress { get; set; }
}
}
UPDATED: The prior code is an update from my previous code (removed a lotta fluff just to get down to the ONE example). Though, for some reason Address (within OfficeAddressField) is never ever updated even though I set the binding of OAddress to the AddressField object.
You don't need to refresh the DataContext. In fact, your control shouldn't be using it. Make Address a DependencyProperty and then use a TemplateBinding in your control's template:
<TextBox Text="{TemplateBinding Address.Address1}"/>
You should consider raising an event when the address dp changes.
Related
I've got a ListBox in WPF where I'm adding items through an OpenFileDialog. Here the Code where I add the Items to my ListBox. (tbxFiles = my listbox name)
if (openFileDialog.ShowDialog() == true)
{
foreach (string filename in openFileDialog.FileNames)
{
path.Add(filename);
for (int i = 0; i < path.Count; i++)
{
tbxFiles.Items.Add(path[i]);
}
}
var _items = this.tbxFiles.Items.Cast<string>().Distinct().ToArray();
this.tbxFiles.Items.Clear();
foreach (var item in _items)
{
this.tbxFiles.Items.Add(item);
}
}
When I look at the breakpoint I can see that the content of what I added to my ListBox is correct. The problem starts showing as I added this DataTemplate to my ListBox:
<ListBox Grid.Column="1" BorderBrush="Black" Margin="15,20,31,15" MinHeight="25" Name="tbxFiles" VerticalAlignment="Stretch" ItemsSource="{Binding Items}" SelectionMode="Multiple">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="ListText" Grid.Column="0"/>
<RadioButton Grid.Column="1" Content="TF" />
<RadioButton Grid.Column="2" Content="AF" />
<ComboBox Grid.Column="3" Text="Periode" />
<Button Grid.Column="4" Click="RemoveMark_Click" Content="Delete" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
The moment I added this DataTemplate it stopped showing me the text, even when I remove the Items from the DataTemplate it isn't showing anything. Maybe I should start showing the text in the textbox I created, but I cannot adress it's name because it is in a DataTemplate. Have any of you an idea of how I can show the name I want to have in the ListBox?
Use binding for your ListBox,
change XAML source to:
<ListBox Grid.Column="1" BorderBrush="Black" Margin="15,20,31,15" MinHeight="25" Name="tbxFiles"
VerticalAlignment="Stretch"
ItemsSource="{Binding Items}"
SelectionMode="Multiple">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="ListText" Grid.Column="0" Text="{Binding FileName}"/>
<RadioButton Grid.Column="1" Content="TF" />
<RadioButton Grid.Column="2" Content="AF" />
<ComboBox Grid.Column="3" Text="Periode" />
<Button Grid.Column="4" Click="RemoveMark_Click" Content="Delete" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
and add two View model classes:
public class ViewModel
{
public List<FileItem> Items { get; set; }
}
public class FileItem
{
public string FileName { get; set; }
}
and you may bind loading file names to your ListBox like this:
var vm = new ViewModel();
vm.Items = new List<FileItem>();
if (openFileDialog.ShowDialog() == true)
{
foreach (string filename in openFileDialog.FileNames)
{
vm.Items.Add(new FileItem { FileName = filename });
}
tbxFiles.DataContext = vm;
}
In a proper MVVM approach you would however have a view model like this:
public class ViewModel
{
public ObservableCollection<FileItem> Items { get; }
= new ObservableCollection<FileItem>();
}
with a readonly ObservableCollection property, which is once assigned to the DataContext of the Window, e.g. like
private readonly ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
}
and used like this:
if (openFileDialog.ShowDialog() == true)
{
viewModel.Items.Clear();
foreach (string filename in openFileDialog.FileNames)
{
viewModel.Items.Add(new FileItem { FileName = filename });
}
}
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
Trying to do simple example of using master detail in windows universal project. But VS shows an error: DataType "Any Name" is not supported in a Windows Universal project. Or No Data Type defined for Data Template if there no Data Type. Can not find answer in stackoverflow or elsewhere. Can you help me?
Code:
MasterDetailPage.xaml
<Page
x:Class="App1.Pages.MasterDetailPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
NavigationCacheMode="Enabled"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Transitions>
<TransitionCollection>
<NavigationThemeTransition />
</TransitionCollection>
</Page.Transitions>
<Page.Resources>
<DataTemplate x:Key="MasterListViewItemTemplate" x:DataType="viewmodels:ItemViewModel">
<Grid Margin="0,11,0,13">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind Title}" Style="{ThemeResource BaseTextBlockStyle}" />
<TextBlock
Text="{x:Bind Text}"
Grid.Row="1"
MaxLines="1"
Style="{ThemeResource ListBodyTextBlockStyle}" />
<TextBlock
Text="{x:Bind DateCreatedHourMinute}"
Grid.Column="1"
Margin="12,1,0,0"
Style="{ThemeResource ListCaptionAltTextblockStyle}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="DetailContentTemplate" x:DataType="viewmodels:ItemViewModel">
<StackPanel>
<TextBlock
Margin="0,8"
Style="{ThemeResource TitleTextBlockStyle}"
HorizontalAlignment="Left"
Text="{x:Bind Title}"/>
<TextBlock
Margin="0,9"
HorizontalAlignment="Left"
MaxWidth="560"
Style="{ThemeResource BodyTextBlockStyle}"
Text="{x:Bind Text}" />
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="AdaptiveStates" CurrentStateChanged="AdaptiveStates_CurrentStateChanged">
<VisualState x:Name="DefaultState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="NarrowState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MasterColumn.Width" Value="*" />
<Setter Target="DetailColumn.Width" Value="0" />
<Setter Target="MasterListView.SelectionMode" Value="None" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="MasterColumn" Width="320" />
<ColumnDefinition x:Name="DetailColumn" Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Text="My Items"
Margin="12,8,8,8"
Style="{ThemeResource TitleTextBlockStyle}" />
<ListView
x:Name="MasterListView"
Grid.Row="1"
ItemContainerTransitions="{x:Null}"
ItemTemplate="{StaticResource MasterListViewItemTemplate}"
IsItemClickEnabled="True"
ItemClick="MasterListView_ItemClick">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<ContentPresenter
x:Name="DetailContentPresenter"
Grid.Column="1"
Grid.RowSpan="2"
BorderThickness="1,0,0,0"
Padding="24,0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}"
ContentTemplate="{StaticResource DetailContentTemplate}">
<ContentPresenter.ContentTransitions>
<!-- Empty by default. See MasterListView_ItemClick -->
<TransitionCollection />
</ContentPresenter.ContentTransitions>
</ContentPresenter>
</Grid>
ItemViewModel
using App1.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Globalization.DateTimeFormatting;
namespace App1.ViewModels
{
public class ItemViewModel
{
private int _itemId;
public int ItemId
{
get
{
return _itemId;
}
}
public string DateCreatedHourMinute
{
get
{
var formatter = new DateTimeFormatter ("hour minute");
return formatter.Format (DateCreated);
}
}
public string Title
{
get; set;
}
public string Text
{
get; set;
}
public DateTime DateCreated
{
get; set;
}
public ItemViewModel ()
{
}
public static ItemViewModel FromItem (Item item)
{
var viewModel = new ItemViewModel ();
viewModel._itemId = item.Id;
viewModel.DateCreated = item.DateCreated;
viewModel.Title = item.Title;
viewModel.Text = item.Text;
return viewModel;
}
}
}
need more of this code?
You are missing a namespace declaration for your ViewModels namespace in your Xaml.
Add at the top:
xmlns:viewmodels="using:App1.ViewModels"
Imagine it like a using statement for Xml. Once you set that, you can use the classes from App1.ViewModels using viewmodel: in Xml. Without that, the parser doesn't even know these classes exist.
I'm trying to add an additional feature to my custom control spinner but I get an error when I attempt to add another command. Anyone who has experience with this, could you possibly explain why this continues to error out. You can find the line in question by just searching for 'MouseUp' which is near the lower 3rd of the XAML. When i attempt to add it, it crashes.
The error:
Exception thrown: 'System.Windows.Markup.XamlParseException' in
PresentationFramework.dll
Numeric.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:btl="clr-namespace:NumericSpinner"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/NumericSpinner;component/Resources/AllBrushes.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type btl:NumericSpinnerControl}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type btl:NumericSpinnerControl}">
<Grid Background="{Binding Path=Background, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" >
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<!--
we use the TemplatedParent binding to get a reference to the control
this template has been applied to, so we can access the property on it
-->
<TextBox Grid.Row="0"
Grid.Column="0"
Width="Auto"
Margin="0,0,1,0"
VerticalAlignment="Center"
BorderThickness="0"
BorderBrush="Transparent"
IsReadOnly="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=IsReadOnly,
Mode=TwoWay}"
Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=FormattedValue,
Mode=TwoWay,
NotifyOnTargetUpdated=True,
NotifyOnSourceUpdated=True,
UpdateSourceTrigger=LostFocus,
NotifyOnValidationError=True}"
btl:TextBoxBehavior.SelectAllTextOnFocus ="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=SelectAllOnGotFocus,
Mode=TwoWay}"
>
</TextBox>
<Grid x:Name="grid1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<RepeatButton Grid.Row="0"
Grid.Column="1"
Width="22"
Height="11"
Background="Transparent"
IsEnabled="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=CanIncrement}"
Command="{x:Static btl:NumericSpinnerControl.IncreaseCommand}"
MouseUp="{x:Static btl:NumericSpinnerControl.MouseUpCommand}">
<RepeatButton.Content>
<Rectangle Width="16"
Height="5"
Fill="{StaticResource BrushScrollUp}" />
</RepeatButton.Content>
<RepeatButton.InputBindings>
<MouseBinding MouseAction="RightClick" Command="{x:Static btl:NumericSpinnerControl.SetValueToMaximumCommand}"/>
</RepeatButton.InputBindings>
</RepeatButton>
<RepeatButton Grid.Row="1"
Grid.Column="1"
Width="22"
Height="11"
Background="Transparent"
IsEnabled="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=CanDecrement}"
Command="{x:Static btl:NumericSpinnerControl.DecreaseCommand}">
<RepeatButton.Content>
<Rectangle Width="16"
Height="5"
Fill="{StaticResource BrushScrollDown}" />
</RepeatButton.Content>
<RepeatButton.InputBindings>
<MouseBinding MouseAction="RightClick" Command="{x:Static btl:NumericSpinnerControl.SetValueToMinimumCommand}"/>
</RepeatButton.InputBindings>
</RepeatButton>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Resource dictionary entries should be defined here. -->
</ResourceDictionary>
The commands being called are these:
public static RoutedCommand IncreaseCommand { get; set; }
protected static void OnIncreaseCommand(Object sender, ExecutedRoutedEventArgs e)
{
NumericSpinnerControl control = sender as NumericSpinnerControl;
if (control != null)
{
Console.WriteLine("Pressed");
control.OnIncrease();
}
}
protected void OnIncrease()
{
Value = LimitValueByBounds(Value + Increment, this);
}
public static RoutedCommand MouseUpCommand { get; set; }
protected static void OnMouseUpCommand(Object sender, ExecutedRoutedEventArgs e)
{
NumericSpinnerControl control = sender as NumericSpinnerControl;
if (control != null)
{
Console.WriteLine("mouse up");
}
}
private static void InitializeCommands()
{
// create instances
IncreaseCommand = new RoutedCommand("IncreaseCommand", typeof(NumericSpinnerControl));
MouseUpCommand = new RoutedCommand("MouseUpCommand", typeof(NumericSpinnerControl));
// register the command bindings - if the buttons get clicked, call these methods.
CommandManager.RegisterClassCommandBinding(typeof(NumericSpinnerControl), new CommandBinding(IncreaseCommand, OnIncreaseCommand));
CommandManager.RegisterClassCommandBinding(typeof(NumericSpinnerControl), new CommandBinding(MouseUpCommand, OnMouseUpCommand));
}
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();
}
}