I have a WPF Window that is meant to be used as a dialog. I am trying to use MVVM and have a view model as the DataContext of this dialog. So the simple confirmation dialog looks like:
<Window x:Class="Provision.Views.ConfirmationDialog"
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:Provision.Views"
mc:Ignorable="d"
Title="ConfirmationDialog" Height="450" Width="800"
DialogResult="{Binding DialogResult, Mode=TwoWay}">
<DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Margin="12" HorizontalAlignment="Right">
<Button Content ="{Binding Resources.Ok, FallbackValue=Ok}" IsDefault="True" Command="{Binding OkCommand}" Margin="12,0" Padding="12,6"/>
<Button Content ="{Binding Resources.Cancel, FallbackValue=Cancel}" IsCancel="True" Command="{Binding CancelCommand}" Padding="12,6"/>
</StackPanel>
<TextBlock Text="{Binding Resources.Confirmation, FallbackValue='Are you sure?'}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</DockPanel>
</Window>
The problem I am having is binding to DialogResult. I get an error message
System.Windows.Markup.XamlParseException: 'A 'Binding' cannot be set on the 'DialogResult' property of type 'ConfirmationDialog'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.'
The property in the view model that it is binding to looks like:
private bool _dialogResult = false;
public bool DialogResult
{
get => _dialogResult;
set
{
if (_dialogResult != value)
{
_dialogResult = value;
RaisePropertyChanged();
}
}
}
I would eventually like to do something like
var result = new ConfirmationDialog().ShowDialog()
But I guess I don't understand the error message or how to overcome it. Can I make it a DependencyProperty in the view model? If so how?
Related
I would like to have a popup show at the bottom of each textbox in my window, as they are focused.
The user would be presented with the last few entries entered in that textbox. I would like the placement to be such that it would be at the bottom of the textbox currently focused.
This is my user control with the textbox:
<UserControl x:Class="PopupPlacement.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Name="TextBox_MyControl" Text="enter your text here" Height="25" Width="200"/>
</StackPanel>
</UserControl>
Here is my window:
<Window x:Class="PopupPlacement.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PopupPlacement"
Title="MainWindow" Height="450" Width="800">
<Canvas>
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Content="Domain" Margin="10"/>
<local:MyControl Grid.Column="1" x:Name="Domain" Margin="10"/>
<Label Grid.Row="1" Content="Username" Margin="10"/>
<local:MyControl Grid.Row="1" Grid.Column="1" x:Name="Username" Margin="10"/>
<Label Grid.Row="2" Content="Password" Margin="10"/>
<local:MyControl Grid.Row="2" Grid.Column="1" x:Name="Password" Margin="10"/>
<Button Grid.Row="3" Content="OK" Margin="10" Name="Button_OK"/>
<Button Grid.Row="3" Grid.Column="1" Content="Cancel" Margin="10"/>
<Popup PlacementTarget="{Binding ElementName=TextBox_MyControl}" Placement="Bottom"
IsOpen="{Binding ElementName=TextBox_MyControl, Path=IsKeyboardFocused}">
<ComboBox IsDropDownOpen="True">
<ComboBoxItem IsSelected="True">Item 1</ComboBoxItem>
<ComboBoxItem>Item 2</ComboBoxItem>
</ComboBox>
</Popup>
</Grid>
</Canvas>
</Window>
Appreciate any pointers.
For me, the best solution to a similar requirement was to write a Behavior that kind of mimics Intellisense.
I don't have any simple code at hand, but you could create and show a ListBox inside a Popup placed at the AssociatedObject's bottom. You can then bind the TextBox-related entries to the Behavior via a DependencyProperty.
Of course, there's a lot more to it like closing the Popup, re-using existing controls, handling key presses to access the ListBox, insert the selected value to the TextBox etc.
Here's a simple (untested) sketch.
public class IntellisenseBehavior : Behavior<TextBox>
{
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(IntellisenseBehavior), new UIPropertyMetadata(null));
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotKeyboardFocus += AssociatedObjectOnGotKeyboardFocus;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotKeyboardFocus -= AssociatedObjectOnGotKeyboardFocus;
//cleanup
}
private void AssociatedObjectOnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var popup = new Popup
{
ClipToBounds = false,
AllowsTransparency = true,
PopupAnimation = PopupAnimation.Fade,
HorizontalAlignment = HorizontalAlignment.Left
};
popup.SetValue(FocusManager.IsFocusScopeProperty, true);
popup.Placement = PlacementMode.Bottom;
popup.PlacementTarget = AssociatedObject;
var shadow = new SystemDropShadowChrome { Color = Colors.Transparent, MaxHeight = 200, Margin = new Thickness(0, 0, 5, 5) };
var listBox = new ListBox
{
ItemsSource = ItemsSource
}
((IAddChild)shadow).AddChild(listBox);
((IAddChild)popup).AddChild(shadow);
popup.IsOpen = true;
}
}
Attach it to all TextBoxes that you require to have this functionality and for instance use a converter to get the filtered entries you need.
<!-- Uses converter's public const string NameBox = "NameBox"; for filtering. -->
<TextBox>
<i:Interaction.Behaviors>
<IntellisenseBehavior ItemsSource="{Binding LastEntries, Converter={StaticResource FilterEntriesConverter}, ConverterParameter={x:Static FilterEntriesConverter.NameBox}}" />
</i:Interaction.Behaviors>
</TextBox>
Hope that helps.
I'm making an application using Caliburn.Micro(for easy data binding and stuff) and MahApps.Metro(for designing).
I've created a View name 'MainView' which has HamburgerMenu of MahApps.
My issue is data binding is working fine under HamburgerMenu.ContentTemplate tag
Here is my HamburgerMenu.ContentTemplate xaml.
<Page x:Class="Sample.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:utils="clr-namespace:Omni.WindowsClient.Utils"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Omni.WindowsClient.Views"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="600">
<Page.Resources>
<DataTemplate x:Key="HamburgerMenuItem"
DataType="{x:Type mah:HamburgerMenuItem}">
<Grid Height="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Margin="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Glyph}"
Stretch="UniformToFill" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{Binding Label}" />
</Grid>
</DataTemplate>
</Page.Resources>
<Grid>
<mah:HamburgerMenu x:Name="HamburgerMenuControl"
SelectedIndex="0"
ItemTemplate="{StaticResource HamburgerMenuItem}"
OptionsItemTemplate="{StaticResource HamburgerMenuItem}"
IsPaneOpen="True"
DisplayMode="CompactInline"
cal:Message.Attach="[Event ItemClick] = [Action ShowDetails(HamburgerMenuControl.SelectedItem)]"
DataContext="{Binding RelativeSource={RelativeSource self}}">
<mah:HamburgerMenu.ItemsSource>
<mah:HamburgerMenuItemCollection>
<mah:HamburgerMenuItem Label="System Status">
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Tasks" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
<mah:HamburgerMenuItem Label="Inbox">
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Inbox" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Certificate" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
</mah:HamburgerMenuItemCollection>
</mah:HamburgerMenu.ItemsSource>
<mah:HamburgerMenu.ContentTemplate>
<DataTemplate DataType="{x:Type mah:HamburgerMenuItem}">
<Grid utils:GridUtils.RowDefinitions="48,*">
<!--cal:Action.TargetWithoutContext="{Binding ElementName=HamburgerMenuControl, Path=DataContext}"-->
<Border Grid.Row="0"
Background="{DynamicResource MahApps.Metro.HamburgerMenu.PaneBackgroundBrush}">
<TextBlock x:Name="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"
Foreground="White" />
<!--Text="{Binding Path=Header, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"-->
</Border>
<Frame Grid.Row="1"
cal:Message.Attach="RegisterFrame($source)"
DataContext="{x:Null}"
NavigationUIVisibility="Hidden" />
</Grid>
</DataTemplate>
</mah:HamburgerMenu.ContentTemplate>
</mah:HamburgerMenu>
</Grid>
</Page>
and respective view model code is:
using Caliburn.Micro;
using MahApps.Metro.Controls;
using System.Windows.Controls;
namespace Sample.ViewModels
{
public class MainViewModel : Screen
{
private readonly SimpleContainer _container;
private INavigationService _navigationService;
private string _header;
public string HeaderTitle
{
get { return _header; }
set
{
_header = value;
NotifyOfPropertyChange();
}
}
public MainViewModel(SimpleContainer container)
{
this._container = container;
DisplayName = "Main";
}
public void RegisterFrame(Frame frame)
{
_navigationService = new FrameAdapter(frame);
_container.Instance(_navigationService);
_navigationService.NavigateToViewModel(typeof(SystemStatusViewModel));
HeaderTitle = "System Status";
}
public void ShowDetails(HamburgerMenuItem menuItem)
{
switch (menuItem.Label)
{
case "System Status":
_navigationService.NavigateToViewModel(typeof(SystemStatusViewModel));
HeaderTitle = "System Status";
break;
case "Inbox":
_navigationService.NavigateToViewModel(typeof(InboxViewModel));
HeaderTitle = "Inbox";
break;
default:
break;
}
}
}
}
I want to change View in frame under HamburgerMenu.ContentTemplate when I click on menu item.
Like System Status view is SystemStatusView
and Inbox view is InboxView.
My code is working fine (it changes the view in frame and change the Header label too) if I don't use HamburgerMenu.ContentTemplate. But I want to use HamburgerMenu.ContentTemplate to work with HamburgerMenu.
Thanks!
If it's working fine if you don't use HamburgerMenu.ContentTemplate, but stops working when you do, the problem is probably with you overwriting the default template in a way that doesn't support all functionalities of a control.
I'd suggest you to use Blend to get the default HamburgerMenu.ContentTemplate, then just edit it to your needs, without changing too much (keep in mind that names of controls used as a template may have a crucial meaning, so be careful what you are editing).
If you don't know how to use Blend to get your control's template, here is a simple tutorial described in a documentation of Telerik controls (don't worry, it works the same for all controls). You just need to create copy of a HamburgerMenu.ContentTemplate, paste it to your application and you are good to go (editing).
I have a UserControl with a TextBox inside like this:
<UserControl x:Class="xxx.CommonControls.Views.InPlaceEdit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="parent"
mc:Ignorable="d">
<Border Background="Transparent" MouseLeftButtonDown="UIElement_OnMouseLeftButtonDown">
<TextBox x:Name="ButtonEdit"
Text="{Binding ElementName=parent,
Path=Value,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
</TextBox >
</Border>
</UserControl>
Usage:
<controls:InPlaceEdit Grid.Row="0"
Grid.Column="1"
Height="25"
Margin="5,0,0,5"
Value="{Binding SelectedPatient.Name, UpdateSourceTrigger=PropertyChanged}" />
Question is: how to move Validation errors to my TextBox? It's because all ValidationErrors are stored in my InPlaceEdit control, and not populated properly to TextBox.
(It's simplified view, but basically shows my problem. In my application I'm using ButtonEdit from DevExpress instead TextBox)
I have done it by removing Text binding from my child control and setting a binding to be exactly same as a parent:
this.Loaded += (sender, args) =>
{
var binding = BindingOperations.GetBinding(this, ValueProperty);
if (binding != null)
{
BindingOperations.SetBinding(ButtonEdit, DevExpress.Xpf.Editors.TextEditBase.TextProperty, binding);
}
};
I have been struggling with this for three days now and I feel I am very close to a solution, but I just can't get there.
I am making a sudoku puzzle and I would like to create a custom control to display one of the nine 3x3 grids, so I dan display nine of them and have a nice 9x9 grid.
I have found at least 30 different pages that should explain how to create this but I could not find the solution on each of them.
I think the problem lays in the PartialSudokuGrid because the Values property doesn't seem to get called. Also, no errors are displayed in the output window. Can anyone tell me what I am doing wrong?
Not meaning to dump code and expect someone to fix it, but I am really stuck on this and I feel as if it is just a little change that will make everything work.
Here is my code:
MainWindow:
<Window x:Class="SudokuWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SudokuWPF"
Title="MainWindow" Height="400" Width="400"
DataContext="{Binding PartialSudokuGrid, Source={StaticResource Locator}}">
<UniformGrid Columns="3" Rows="3">
<local:PartialSudokuGrid Values="{Binding ValuesVM}" />
</UniformGrid>
</Window>
ViewModel:
public class PartialSudokuGridVM : ViewModelBase {
private int[] _values;
public PartialSudokuGridVM() {
this.ValuesVM = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
public int[] ValuesVM {
get {
return this._values;
}
set {
this._values = value;
this.RaisePropertyChanged();
}
}
}
UserControl:
<UserControl x:Class="SudokuWPF.PartialSudokuGrid"
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="300" d:DesignWidth="300"
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=Values}">
<UniformGrid>
<TextBox Text="{Binding [0]}" />
<TextBox Text="{Binding [1]}" />
<TextBox Text="{Binding [2]}" />
<TextBox Text="{Binding [3]}" />
<TextBox Text="{Binding [4]}" />
<TextBox Text="{Binding [5]}" />
<TextBox Text="{Binding [6]}" />
<TextBox Text="{Binding [7]}" />
<TextBox Text="{Binding [8]}" />
</UniformGrid>
</UserControl>
Code behind:
public partial class PartialSudokuGrid : UserControl {
public PartialSudokuGrid() {
InitializeComponent();
}
public int[] Values {
get {
return (int[])GetValue(ValuesProperty);
}
set {
SetValue(ValuesProperty, value);
}
}
public static DependencyProperty ValuesProperty = DependencyProperty.Register("Values", typeof(int[]), typeof(PartialSudokuGrid));
}
Fix:
Like MDoobie suggested, I removed the Self binding from the PartialGridView and cleared the codebehind file (no use anymore).
old:
<local:PartialSudokuGrid Values="{Binding ValuesVM}" />
new:
<local:PartialSudokuGrid DataContext="{Binding ValuesVM}" />
I think you set the Window's DataContext with this line DataContext="{Binding PartialSudokuGrid, Source={StaticResource Locator}}"
It is set the PartialSudokuGrid not the PartialSudokuGridVM (which has the ValuesVM property). Try to set the PartialSudokuGridVm as DataContext.
I have following xaml code:
<Window x:Class="WPF_les_3.Oefening_4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Oefening_4" Height="300" Width="300">
<StackPanel Width="auto" Margin="20px">
<ComboBox Width="100" SelectionChanged="ComboBox_Selected" x:Name="comboBox">
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Red" Height="20" Width="20"/>
<TextBlock Text=" Red"/>
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Yellow" Height="20" Width="20"/>
<TextBlock Text=" Yellow"/>
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Green" Height="20" Width="20"/>
<TextBlock Text=" Green"/>
</StackPanel>
</ComboBoxItem>
</ComboBox>
</StackPanel>
As you see, inside my ComboboxItems I have a rectangle and a textblock. Now I want to retreive the fill color of the rectangle (or the text of the textblock, it's the same) when my selectionchanged event is handled, so I can change the background of the window according to the selected color (which is the goal of the excercise).
To elaborate on my comment above, this is the Correct way to achieve what you need in WPF:
First of all, create a proper ViewModel that contains the list of available colors and a SelectedColor property:
public class ColorsViewModel
{
public ObservableCollection<string> Colors { get; private set; }
private string _selectedColor;
public string SelectedColor
{
get { return _selectedColor; }
set
{
_selectedColor = value;
MessageBox.Show("Selected Color: " + value); //message box here to show the code is actually working.
}
}
//... More code here in a moment
}
Then, make sure you populate the color collection with relevant data. In the case of colors specifically, WPF has built-in TypeConverters that can convert from (for example) string to System.Windows.Media.Color implicitly, therefore we can leverage that to simplify our code and use simple strings:
//Continuation of the above code
public ColorsViewModel()
{
Colors = new ObservableCollection<string>
{
"Red",
"Green",
"Blue",
"Yellow",
};
}
And finally create the UI in XAML using proper DataBinding:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ComboBox ItemsSource="{Binding Colors}"
SelectedItem="{Binding SelectedColor}"
VerticalAlignment="Center" HorizontalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding}" Height="20" Width="20"/>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Window>
Result:
The change event is fired and the ComboBox.SelectedItem has the info you need.
You have to analyze the SelectedItem like my following method:
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem comboBoxItem = this.comboBox.SelectedItem as ComboBoxItem;
if (comboBoxItem != null)
{
StackPanel stackPanel = comboBoxItem.Content as StackPanel;
if(stackPanel != null && stackPanel.Children[0] is Rectangle)
{
var fill = (stackPanel.Children[0] as Rectangle).Fill;
}
}
}
Here you get the fill of the rectangle and can handle this or do your stuff.
But be patient, this code is created exactly for you sample (ComboBoxItem with Content StackPanel with Children[0] as Rectangle). Changes will iterrupt the process ;)