New window as CommandParameter every time - c#

I want a button to show app settings window like this:
<Window.Resources>
<local:SettingsWindow x:Key="SettingsWnd"/>
</Window.Resources>
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<Button Command="{Binding ShowSettingsCommand}"
CommandParameter="{DynamicResource SettingsWnd}"/>
The ViewModel kinda thing:
class MyViewModel : BindableBase
{
public MyViewModel()
{
ShowSettingsCommand = new DelegateCommand<Window>(
w => w.ShowDialog());
}
public ICommand ShowSettingsCommand
{
get;
private set;
}
}
The problem is that it only works one time because you can't reopen previously closed windows. And apparently, the XAML above does not make new instances to open like that.
Is there a way to pass new window as CommandParameter every time the command is called?

Does this converter solve your problem?
class InstanceFactoryConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var type = value.GetType();
return Activator.CreateInstance(type);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
...
<Window.Resources>
<local:SettingsWindow x:Key="SettingsWnd"/>
<local:InstanceFactoryConverter x:Key="InstanceFactoryConverter"/>
</Window.Resources>
...
<Button Command="{Binding ShowSettingsCommand}"
CommandParameter="{Binding Source={StaticResource SettingsWnd}, Converter={StaticResource InstanceFactoryConverter}}"/>

Related

Accessing UserControl Resource Dictionary from code

I' am trying to read/load a UserControl Resources from my Home.xaml to a ResourceDictionary object so I can dynamicly display it inside content control and can't manage to find a solution.
I dont want to read it from my App.xaml, just the Home.xaml.
In my View folder I have Home.xaml:
<UserControl.Resources>
<converters:CheckBoxConverter x:Key="CheckBoxConv"></converters:CheckBoxConverter>
<DataTemplate x:Key="Punomoc">
<CheckBox Margin="10" Content="Izradi punomoć"
IsChecked="{Binding UnosIOS.Punomoc}">
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="Naknada">
<CheckBox Margin="10,0" Content="Naplata naknade"
IsChecked="{Binding UnosIOS.NaplataNaknade}">
</CheckBox>
</DataTemplate>
</UserControl.Resources>
<Grid>
...
```
...
```
In my ConvertersFolder I have:
public class CheckBoxConverter : IValueConverter
{
private static readonly ResourceDictionary ControlResourceDictionary;
static CheckBoxConverter()
{
ControlResourceDictionary = new ResourceDictionary
{
Source = new Uri("pack://application:,,,/View/Home.xaml", UriKind.Absolute)
};
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DataTemplate dataTemplate =ControlResourceDictionary["Punomoc"] as DataTemplate;
return dataTemplate;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But its returning error:
''The invocation of the constructor on type 'IzdavanjeIOS.Converters.CheckBoxConverter' that matches the specified binding constraints threw an exception.' Line number '23' and line position '10'.'
Also tried with URI string : MyAppName/View/Home.xaml
I have setup MVVM and dont want to access resources from code-behind unless its the only choice, it seems to me that it should be possible like above, but maybe I am giving the wrong URI?

InvalidCastException in design time when specify converter as MarkupExtension

I want to implement value converter to specify Point for RenderTransform. That works fine till I just implement IValueConverter interface. I know that I can implement MarkupExtension class to not to declare separated XAML Resource for my converter each time. When I try to implement this, I have
InvalidCastException: Unable to cast object of type 'System.Object' to type 'System.Windows.Data.IValueConverter'.
Converter implementation:
[ValueConversion(typeof(Point), typeof(Transform))]
public class PointToTransformConverter : MarkupExtension, IValueConverter
{
private PointToTransformConverter _instance = null;
public object Convert(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
var point = (Point)value;
return new TransformGroup
{
Children = new TransformCollection
{
new TranslateTransform(point.X, point.Y)
}
};
}
public object ConvertBack(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
var transform = value as TransformGroup;
if (transform?.Children.Count > 0)
{
var translateTransform = transform.Children[0] as TranslateTransform;
if (translateTransform != null)
{
return new Point(
translateTransform.X,
translateTransform.Y);
}
}
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _instance ?? (_instance = new PointToTransformConverter());
}
}
XAML usage:
<local:PathControl x:Class="PathToWiringTube.PathView"
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"
xmlns:local="clr-namespace:PathToWiringTube"
xmlns:view="clr-namespace:PathToWiringTube.View"
xmlns:vm="clr-namespace:PathToWiringTube.VM"
xmlns:crocodile="clr-namespace:Crocodile;assembly=Crocodile"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance vm:PathVm}">
<local:PathControl.Resources>
<crocodile:PointToTransformConverter x:Key="pointToTransformConverter"/>
</local:PathControl.Resources>
<Canvas>
<ItemsControl x:Name="itemsMarkers" ItemsSource="{Binding Markers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<view:SplineDensityMarker Width="5" RenderTransform="{Binding Position, Converter={StaticResource pointToTransformConverter}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</local:PathControl>
What am I doing wrong?
Could you please try the code below and check if the exception is coming?
I have corrected it. The instance should be static and the xaml, you can directly refer to the converter instead of calling it as a static resource. That's the main use of using a MarkupExtension. The code below should work fine. If any issues, please revert.
Converter :
[ValueConversion(typeof(Point), typeof(Transform))]
public class PointToTransformConverter : MarkupExtension, IValueConverter
{
private static PointToTransformConverter _instance = null;
public object Convert(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
var point = (Point)value;
return new TransformGroup
{
Children = new TransformCollection
{
new TranslateTransform(point.X, point.Y)
}
};
}
public object ConvertBack(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
var transform = value as TransformGroup;
if (transform?.Children.Count > 0)
{
var translateTransform = transform.Children[0] as TranslateTransform;
if (translateTransform != null)
{
return new Point(
translateTransform.X,
translateTransform.Y);
}
}
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _instance ?? (_instance = new PointToTransformConverter());
}
}
Xaml :
<local:PathControl x:Class="PathToWiringTube.PathView"
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"
xmlns:local="clr-namespace:PathToWiringTube"
xmlns:view="clr-namespace:PathToWiringTube.View"
xmlns:vm="clr-namespace:PathToWiringTube.VM"
xmlns:crocodile="clr-namespace:Crocodile;assembly=Crocodile"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance vm:PathVm}">
</local:PathControl.Resources>
<Canvas>
<ItemsControl x:Name="itemsMarkers" ItemsSource="{Binding Markers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<view:SplineDensityMarker Width="5" RenderTransform="{Binding Position, Converter={crocodile:PointToTransformConverter}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</local:PathControl>
I've checked my code on another environment and detected that it works. I have done research for Visual Studio version mismatch and found out it (14.0.23107.0 D14REL and 14.0.24720.00 Update1). After installing the latest Visual Studio Update 2 on my old environment everything works fine.

Shorthand way of specifying strongly typed values in Xaml

Here's what I would like to end up with:
<Grid Visibility={Binding EnablePurchase, Converter={local:ConditionalConverter TrueValue=(Visibility)Visible FalseValue=(Visibility)Collapsed}}/>
Here's currently what I am doing:
<Grid>
<Grid.Visibility>
<Binding Path="EnablePurchase">
<Binding.Converter>
<local:ConditionalConverter>
<local:ConditionalConverter.TrueValue>
<Visibility>Visible</Visibility>
<local:ConditionalConverter.TrueValue>
<local:ConditionalConverter.FalseValue>
<Visibility>Collapsed</Visibility>
<local:ConditionalConverter.FalseValue>
</local:ConditionalConverter>
</Binding.Converter>
</Binding>
</Grid.Visibility>
</Grid>
You can simply create a converter which has properties like this:
public class ValueConverterWithProperties : IValueConverter
{
public int TrueValue { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((int) value == TrueValue)
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and then use it like this:
<Window x:Class="Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converter="clr-namespace:Converter"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<converter:ValueConverterWithProperties TrueValue="5" x:Key="converterWithProperties"></converter:ValueConverterWithProperties>
</Window.Resources>
<Grid>
<CheckBox IsChecked="{Binding item, Converter={StaticResource converterWithProperties}}"></CheckBox>
</Grid>
you can also derive from MarkupExtension in your converter for a much nicer usage:
public class ValueConverterWithProperties : MarkupExtension, IValueConverter
{
public int TrueValue { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((int) value == TrueValue)
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then you can set the property directly where you are using the converter allowing you to set different values per converter easily:
<Window x:Class="Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converter="clr-namespace:Converter"
Title="MainWindow" Height="350" Width="525">
<Grid>
<CheckBox IsChecked="{Binding item, Converter={converter:ValueConverterWithProperties TrueValue=5}}"></CheckBox>
<CheckBox IsChecked="{Binding item2, Converter={converter:ValueConverterWithProperties TrueValue=10}}"></CheckBox>
</Grid>

XAML bind to static method with parameters

I got a static class like the following:
public static class Lang
{
public static string GetString(string name)
{
//CODE
}
}
Now i want to access this static function within xaml as a binding.
Is there such a way for example:
<Label Content="{Binding Path="{x:static lang:Lang.GetString, Parameters={parameter1}}"/>
Or is it necessary to create a ObjectDataProvider for each possible parameter?
Hope someone is able to help me. Thanks in advance!
I get this need too. I "solved" using a converter (like suggested here).
First, create a converter which return the translated string:
public class LanguageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter == null)
return string.Empty;
if (parameter is string)
return Resources.ResourceManager.GetString((string)parameter);
else
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
then use it into XAML:
<Window.Resources>
<local:LanguageConverter x:Key="LangConverter" />
</Window.Resources>
<Label Content="{Binding Converter={StaticResource LangConverter},
ConverterParameter=ResourceKey}"/>
Regards.
The right way would be to go the objectdataprovider route. Although if you are just binding to text rather than use a label, I would use a textblock.
<ObjectDataProvider x:Key="yourStaticData"
ObjectType="{x:Type lang:Lang}"
MethodName="GetString" >
<ObjectDataProvider.MethodParameters>
<s:String>Parameter1</s:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<TextBlock Text={Binding Source={StaticResource yourStaticData}}/>

Preventing almost duplicate RelayCommands in MVVM/MDI app

I'm using an MDI solution (see http://wpfmdi.codeplex.com/) and MVVM.
I use one RelayCommand to bind the toolbar and/or menu to the Main ViewModel, like:
ICommand _editSelectedItemCommand;
public ICommand EditSelectedItemCommand
{
get
{
return _editSelectedItemCommand ?? (_editSelectedItemCommand = new RelayCommand(param => CurrentChildViewModel.EditSelectedItem(),
param => ((CurrentChildViewModel != null) && (CurrentChildViewModel.CanExecuteEditSelectedItem))));
}
}
However, in the child window, to bind a button to the same functionality I need another RelayCommand which is almost equal except it calls the methods EditSelectedItem and CanExecuteEditSelectedItem directly. It would look like:
ICommand _editSelectedItemCommand;
public ICommand EditSelectedItemCommand
{
get
{
return _editSelectedItemCommand ?? (_editSelectedItemCommand = new RelayCommand(param => EditSelectedItem(),
param => CanExecuteEditSelectedItem))));
}
}
I need about 10 and in the future maybe 50 or more of such commands so I like to do it the right way now.
Is there a way to prevent this or a better way to do this?
You can remove the first command from the main viewmodel, because the command in the child viewmodel is more than enough.
Just use the binding like this in the xaml markup:
<Button Command="{Binding CurrentChildViewModel.EditSelectedItemCommand}"
Content="Button for the main view model" />
Also if I understand your code correctly, it has the stipulation that if the CurrentChildViewModel property is null than the command will be disabled.
If you need such behavior, you should add this converter to your code and slightly rewrite the binding:
public class NullToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<Application.Resources>
<local:NullToBooleanConverter x:Key="NullToBooleanConverter" />
</Application.Resources>
<!-- your control -->
<Button Command="{Binding CurrentChildViewModel.EditSelectedItemCommand}"
IsEnabled="{Binding CurrentChildViewModel, Converter={StaticResource NullToBooleanConverter}}" />

Categories

Resources