I am trying to build a custom user control, specifically a custom TextBox that converts the text entered by the user to uppercase as it is typed and displays it in the control. However, I cannot get this to work. Here is my code:
CustomTextBox UserControl:
<UserControl x:Class="SOFWpf.CustomTextBox"
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:converters="clr-namespace:SOFWpf.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<converters:CaseConverter x:Key="CaseConverter" />
</UserControl.Resources>
<TextBox Text="{Binding Path=., Converter={StaticResource CaseConverter}}"/>
UserControl's Code-Behind:
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(CustomTextBox), new UIPropertyMetadata(""));
Usage:
<local:CustomTextBox Text="a b c"/>
Converter:
public class CaseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string text = value as string;
return text?.ToUpper();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string text = value as string;
return text?.ToLower();
}
}
What do I need to change to make this custom TextBox work as intended?
You need to add binding Path=Text:
<TextBox Text="{Binding Path=Text, Converter={StaticResource CaseConverter}}"/>
And in the user control constructor set DataContext to this:
public CustomTextBox()
{
InitializeComponent();
DataContext = this;
}
Related
I have a WPF App and i want to keep all the objects(textboxes,labels and datagrid) within scale/ratio. So i created a class within the app to code a ratio converter as seen below (Not MVVM, NO MODEL CLASSES)
namespace WpfApp2
{
[ValueConversion(typeof(string), typeof(string))]
public class RatioConverter : MarkupExtension, IValueConverter
{
private static RatioConverter _instance;
public RatioConverter() { }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ // do not let the culture default to local to prevent variable outcome re decimal syntax
double size = System.Convert.ToDouble(value) * System.Convert.ToDouble(parameter, CultureInfo.InvariantCulture);
return size.ToString("G0", CultureInfo.InvariantCulture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{ // read only converter...
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _instance ?? (_instance = new RatioConverter());
}
}
}
Here's the code for the XAML Window where i configured it
< Window x:Class = "WpfApp2.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:WpfApp2"
xmlns:tools="clr-namespace:WpfApp2"
mc:Ignorable="d"
Height="{Binding Source={x:Static SystemParameters.PrimaryScreenHeight}, Converter={local:RatioConverter}, ConverterParameter='0.8' }" Width="{Binding Source={x:Static SystemParameters.PrimaryScreenWidth}, Converter={tools:RatioConverter}, ConverterParameter='0.8' }" WindowStartupLocation="CenterScreen" Title="MainWindow" Background="Aqua">
It doesnt appear to be working. Am I missing something? Is there something not properly configured?
<TextBlock x:Name="ContentHeader" Text="{x:bind Name}" ToolTipService.ToolTip="{x:Bind ContentHeader.Text,Mode=OneWay}"
ToolTipService.Placement="Right" TextTrimming="CharacterEllipsis"
Tapped="ContentHeader_Tapped" >
Condition:
My TextBlock's width is determined based on content and I haven't mentioned any width explicitly
The key point of this problem is to complete the conversion by means of the TextBlock.IsTextTrimmed property. We can set Tooltip = null when IsTextTrimmed = False, and set Tooltip to TextBlock.Text when IsTextTrimmed = True.
We cannot use ConverterParameter to pass a changing value (TextBlock.Text) because it is a simple object, not a DependencyProperty, so we need to create a Converter that can hold the TextBlock.Text value.
Converter
public class TrimConverter : DependencyObject, IValueConverter
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TrimConverter), new PropertyMetadata(""));
public object Convert(object value, Type targetType, object parameter, string language)
{
bool isTrim = System.Convert.ToBoolean(value);
return isTrim ? Text : null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Usage
<Page.Resources>
<local:TrimConverter x:Key="TrimConverter" Text="{Binding ElementName=TestBlock,Path=Text}"/>
</Page.Resources>
<Grid>
<TextBlock TextTrimming="CharacterEllipsis"
x:Name="TestBlock"
ToolTipService.ToolTip="{Binding ElementName=TestBlock,
Path=IsTextTrimmed,
Converter={StaticResource TrimConverter}}"/>
</Grid>
This should solve your problem.
Best regards.
Hello I am trying to make a custom Button with PathData inside it. So far I have managed to view the Path inside it. But my Button is not taking MVVM Commands.
Custom Button XAML
<Button x:Class="My_Class.CustomControls.MyButtonWithPath"
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"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<Grid>
<Path Data="{Binding}" Name="path"/>
</Grid>
</Button>
Button Code Behind
public partial class MyButtonWithPath : Button
{
public MyButtonWithPath()
{
InitializeComponent();
this.DataContext = this;
}
private static string _pathDatay;
public string PathDatay
{
get { return _pathDatay; }
set { _pathDatay = value; }
}
public static readonly DependencyProperty PathDataProperty =
DependencyProperty.Register("PathDatay", typeof(string), typeof(MyButtonWithPath), new PropertyMetadata(pathDataCallback));
private static void pathDataCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
_pathDatay = (string)e.NewValue;
}
}
Usage In XAML
<converters:KeyToValueConverter x:Key="conv"/>
<custom:MyButtonWithPath PathDatay="{Binding ConverterParameter=tv, Converter={StaticResource conv}}" />
Converter
public class KeyToValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return Utils.GetPathData(parameter.ToString());
}
catch (Exception c)
{
throw c;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
Utils Class
public static string GetPathData(string key)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("tv", "etc etc etc......");
return dictionary[key];
}
The Problem
Whenever I am writing a Command in the Usage of the Button, it shows a not found error as it looks for the command inside my Custom Button NOT my MainViewModel(FYI I have a ViewModel named MainViewModel where I will put the Command related codes)
My Guess
I am setting the DataContext of the Button to itself with this "this.DataContext=this;" But if I omit this line then the Path does not show. Please guide me to set these things correctly
Solution
Given below
You are right, this.DataContext=this; this line is the problem.
You typically dont use databinding for setting the look of the custom control. Instead you should set the Name of Path and set its Data property on initialization. You can do this by overriiding OnApplyTemplate:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Path p = GetTemplateChild("path") as Path;
p.Data = ...
}
I messed up with the code, #Domysee guided me the right way. My corrections--
Custom Button XAML (Not Needed)
Button Code Behind, Usage In XAML, Converter, Utils (Same as Before)
Additional Part (StyleTemplate)
<Style x:Key="MyButtonWithPathStyle" TargetType="CustomControls:MyButtonWithPath">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CustomControls:MyButtonWithPath">
<Grid Background="Transparent">
<Path x:Name="path" Data="{Binding}" DataContext="{TemplateBinding PathDatay}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
That's it! DataContext="{TemplateBinding PathDatay}" did the trick! Thanks everyone
I understand that the Visibility property of a control cannot be bound to data in the same way that other properties can. It needs some kind of converter(?). In trying to implement the solution from this question I run into a compiler error that says: The resource "BoolToVisible" could not be resolved. I'm guessing that I have to create a ResourceKey named BoolToVisible, I just don't know how.
I'm requesting that someone show me the right way to Bind to the Visibility property of a control.
*The control that I am adding this to is a radio button.
* I have a bool property for isVisible in my Data Model that will be bound to this radio button.
Data Model Property:
private bool _isVisible = true;
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
NotifyPropertyChange(() => IsVisible);
}
}
XAML:
<RadioButton Visibility="{Binding DataModel.IsVisible,Converter={StaticResource ResourceKey=BoolToVisible},RelativeSource={RelativeSource TemplatedParent}}" ... />
Thank you.
2 examples :
The first using a Converter like stated in the question :
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || !(value is bool))
return Binding.DoNothing;
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
in xaml :
<Window x:Class="Stackoverflow.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"
xmlns:local="clr-namespace:Stackoverflow"
>
<Window.Resources>
<local:BooleanToVisibilityConverter x:Key="booleanToVisibiltyConverter"/>
</Window.Resources>
<Grid>
<Button Visibility="{Binding IsSomeThing,Converter={StaticResource booleanToVisibiltyConverter}}"/>
</Grid>
the second :
in your DataContext you can literly hold a Visibility Property
cs :
private Visibility _myControlVisibility;
public Visibility MyControlVisibility
{
get { return _myControlVisibility; }
set { _myControlVisibility = value; }
}
xaml :
<Button Visibility="{Binding MyControlVisibility}"/>
You can binding visibility with a property, you just need to have a Dependency Property of Visibility field as:
Public Property MyVisibility As Windows.Visibility
Get
Return GetValue(MyVisibilityProperty)
End Get
Set(ByVal value As Windows.Visibility)
SetValue(MyVisibilityProperty, value)
End Set
End Property
Public Shared ReadOnly MyVisibilityProperty As DependencyProperty = _
DependencyProperty.Register("MyVisibility", _
GetType(Windows.Visibility), GetType(MyWindow), _
New PropertyMetadata(Nothing))
Then do the binding as the usual (The code is in VB).
Remember that in the New PropertyMetadata you can set an initial state for the object eg:
Public Shared ReadOnly MyVisibilityProperty As DependencyProperty = _
DependencyProperty.Register("MyVisibility", _
GetType(Windows.Visibility), GetType(MyWindow), _
New PropertyMetadata(Windows.Visibility.Hidden))
Please following my code :
<Grid DataContext="{Binding ElementName=dataGrid_services, Path=SelectedItem}"
Width="766">
<RadioButton Content="visit" IsChecked="{Binding Path=type_services}"
FontFamily="Tahoma"/>
i want to bind ischecked property from radiobutton but return value is not false or true.
the value is string. please help me how to bind this value?
thanks in advance
Use an IValueConverter.
Given this window containing your radiobutton and associated bindings:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525" x:Name="dataGrid_services">
<Window.Resources>
<local:CheckedConverter x:Key="converter"/>
</Window.Resources>
<Grid DataContext="{Binding ElementName=dataGrid_services, Path=SelectedItem}" Width="766">
<RadioButton Content="visit" IsChecked="{Binding Path=type_services, Converter={StaticResource converter}}" FontFamily="Tahoma"/>
</Grid>
The changes are adding a namespace reference for local (or whichever namespace your converter is in):
xmlns:local="clr-namespace:WpfApplication1"
creating the converter resource:
<Window.Resources>
<local:CheckedConverter x:Key="converter"/>
</Window.Resources>
and using the converter resource:
IsChecked="{Binding Path=type_services, Converter={StaticResource converter}}"
The converter looks like this and simply converts from string to boolean.
public class CheckedConverter : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string typeService = value as string;
if (typeService == "Yes it is")
{
return true;
}
if (typeService == "Nope")
{
return false;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool typeService = (bool)value;
if (typeService)
{
return "Yes it is";
}
else
{
return "Nope";
}
}
}
You'll have to define a value converter to convert from string to boolean and use it with your RadioButton.
public class StringToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Convert.ToBool(value);
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Convert.ToString(value);
}
}
and in XAML, use
<RadioButton Content="visit" IsChecked="{Binding Path=type_services, Converter={StaticResource stringToBoolConverter}}"
FontFamily="Tahoma"/>
where stringToBoolConverter is defined in the resources of a parent element.
<Window.Resources>
<local:StringToBoolConverter x:Key="stringToBoolConverter" />
</Window.Resources>