I am trying to enable a button only if text is present in two text boxes in a WinForm Application.
My Question is -
Can I achieve this using Data Binding?
If so how?
Edit
Please give reasons for downvote.
Update: Since OP want's to work with DataBinding only here is a solution with the desired technique.
You'll need to use a MultiBinding. Add the two Binding instances (one for each TextBox). Then you'll need to implement an IMultiValueConverter that will accept the values produced by the two binding objects and convert them into a single value.
The binding setup would look something like this:
var multiBinding = new MultiBinding();
multiBinding.Bindings.Add(new Binding("Enabled", textBox1, "Text", true));
multiBinding.Bindings.Add(new Binding("Enabled", textBox2, "Text", true));
multiBinding.Converter = new MyMultiValueConverter();
button1.DataBindings.Add(multiBinding);
And the converter implementation would look something like:
public class MyMultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// perform your conversion here and return the final value
// so which value both textBoxes need to have that you return `true` so
// that `button1.Enabled` gets set to `true`
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
That you can use those classes MultiBinding and IMultiValueConverter you have to add a reference of the PresentationFramework lib to your project. Furthermore I suggest you to add:
using System.Windows.Data;
To shorten your code.
Since I already posted an answer and it's a legit working solution in the way OP desired it I don't edit the question, but rather show in a new question another approach.
You could create a computed property and bind the button.1.Enabled property to it. For example, create a textBoxesCorrect property that returns the value, and bind button.1.Enabled to it. The textBoxesCorrect property gets set in the TextChanged() events of those TextBoxes.
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (textBox1.Text == "") //desired text that the textBoxes shell contain
MyData.textBox1Correct = true;
else
MyData.textBox1Correct = false;
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
if (textBox2.Text == "") //desired text that the textBoxes shell contain
MyData.textBox2Correct = true;
else
MyData.textBox2Correct = false;
}
public class MyData
{
public static bool textBox1Correct { get; set; }
public static bool textBox2Correct { get; set; }
public bool textBoxesCorrect
{
get
{
if (textBox1Correct && textBox2Correct)
return true;
else
return false;
}
}
}
So can still work with your DataBinding, but it's an easier solution to implement to work with multiple sources for the binding.
Related
My question is not about serialization I am getting the serialization part to work just fine, but for context I am trying to serialize some properties set by the user in a WPF form, and therefore require a two-way binding (which I can only get to work in one direction: target-to-source updates).
Let's say I have defined an application property that is a container for my properties I want to be serializable:
public SerializableApplicationProperties ThisAppsSerializableProperties { get; set; }
The SerializableApplicationProperties class looks like this:
[Serializable]
public class SerializableApplicationProperties
{
public SerializableApplicationProperties()
{
UserSelectedPreFillFilePath = new ObservableString();
}
public SerializableApplicationProperties(string defaultFilePath)
{
UserSelectedPreFillFilePath = new ObservableString(defaultFilePath);
}
public ObservableString UserSelectedPreFillFilePath { get; set; }
}
I have one property defined so far that I need to establish a two way-binding for in code (yes, in code). Please do not suggest how I can do this in XAML markup, unless you also explain how to do it entirely in code.
The property is "UserSelectedPreFillFilePath", which you can see is of type "ObservableString".
ObservableString Class looks like this:
[Serializable]
public class ObservableString : INotifyPropertyChanged
{
private string _stringValue;
public string StringValue
{
get { return _stringValue; }
set
{
_stringValue = value;
OnPropertyChanged("StringValue");
}
}
public ObservableString() { }
public ObservableString(string value)
{
this._stringValue = value;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
A very simple class that holds a "StringValue" property and implements INotifyChange.
So then, I try to establish a two-way binding between this "UserSelectedPreFillFileLocation" and a text box on the main window.
I want a two-way binding so that before I close down the application I can serialize the UserSelectedPreFillFileLocation property (which should be automatically updated based on what the user puts in the textbox in the window).
Then when the application loads from a file (deserializes) that textbox is auto-populated with the UserSelectedPreFillFileLocation value via the binding.
Second to last thing, my converter class definition:
[ValueConversion(typeof(ObservableString), typeof(string))]
class ObservableStringToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ObservableString myVar = (ObservableString)value;
return myVar.StringValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string myVar = (string)value;
return new ObservableString(myVar);
}
}
Lastly here is the definition of the binding. The binding works target-to-source but not the other way around. I.E. when the user updates the textbox, UserSelectedPreFillFileLocation is updated, but not the other way around.
Binding UserSelectedPreFillFilePathBinding = new Binding();
UserSelectedPreFillFilePathBinding.Path = new PropertyPath(nameof(ThisAppsSerializableProperties.UserSelectedPreFillFilePath));
UserSelectedPreFillFilePathBinding.Source = ThisAppsSerializableProperties;
UserSelectedPreFillFilePathBinding.Converter = new ObservableStringToStringConverter();
UserSelectedPreFillFilePathBinding.Mode = BindingMode.TwoWay;
UserSelectedPreFillFilePathBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
ThisAppInstanceMainWindow.TxtbxPrefillFileLocation.SetBinding(TextBox.TextProperty, UserSelectedPreFillFilePathBinding);
I discovered what I was doing wrong.
I have indeed defined an ObservableString and the observable property that raises the property changed event is the StringValue within the class.
So, in this case I did not even need a converter since the source property I should have defined for the binding was the StringValue, and the source object should have been the UserSelectedPreFillFilePath.
Corrected code that is now working great with the two-way binding:
Binding UserSelectedPreFillFilePathBinding = new Binding();
UserSelectedPreFillFilePathBinding.Path = new PropertyPath(nameof(ThisAppsSerializableProperties.UserSelectedPreFillFilePath.StringValue));
UserSelectedPreFillFilePathBinding.Source = ThisAppsSerializableProperties.UserSelectedPreFillFilePath;
UserSelectedPreFillFilePathBinding.Mode = BindingMode.TwoWay;
UserSelectedPreFillFilePathBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
ThisAppInstanceMainWindow.TxtbxPrefillFileLocation.SetBinding(TextBox.TextProperty, UserSelectedPreFillFilePathBinding);
This question already has an answer here:
File path to file name String converter not working
(1 answer)
Closed 6 years ago.
Hi i am new to C# and WPF. The below is just example not tested.
Lets say i have
String path1 = "C:\Users\user\Desktop\File.ext"
String path2 = "C:\temp\text.txt"
String.Format(path1.Substring(path1.LastIndexOf('\')+1))
String.Format(path2.Substring(path1.LastIndexOf('\')+1))
I want to get the filename from the path without changing the original string and i want to do it preferably in XAML or do a value converter.
I am looking into this to know if I should do a value converter between one Observable collection and ListView or just stick with two Lists. One to contain filepaths in model manually and fileNames to display in a ListView and update both separately (Current approach).
My data model
private List<String> AttachedFiles;
public ObservableCollection<String> fileNames { get; set; }
public void addFilePath(String filePath) {
this.AttachedFiles.Add(filePath);
}
public void removeFilePath(String filePath)
{
this.AttachedFiles.Remove(filePath);
}
The xaml
<ListView x:Name="DropList" ItemsSource="{Binding fileNames}"
I included very little code as this as to give an just an idea as I am thinking of refactoring. Can i use String methods inside String.Format for dipsplaying in XAML so it will still have the original filepath value in the model or is there some syntax for String.Format to do this?
EDIT
The answers are duplicated but I was asking if String.Format can be used in the XAML converter with methods like Trim Substring Etc and if I should refactor my code, but doing it properly creates more code and complexity than it removes.
EDIT 2
This is the code. Basically the list view has an X icon and text with file name next it, if the user clicks the icon the it will call this:
private void r1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Rectangle r = sender as Rectangle;
ticket.removeFilePath(r.DataContext.ToString());
ticket.fileNames.Remove(r.DataContext.ToString());
}
Use one collection, and don't use List<T>:
ObservableCollection<String> FilePaths { get; private set; }
If you replace the collection, you'll need to raise INotifyPropertyChanged.PropertyChanged.
Write a value converter (OK, just steal mine):
public class FileName : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return System.IO.Path.GetFileName(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And use the value converter in a DataTemplate. Since FilePaths is a collection of String, the DataContext of your DataTemplate will be a String, so the binding won't specify a property for the binding to use. The value passed to the value converter will be the string itself.
<ListView x:Name="DropList" ItemsSource="{Binding FilePaths}">
<ListView.ItemTemplate>
<DataTemplate>
<Label
Content="{Binding Converter={local:FileName}}"
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you are dealing with only one property here, you can just use a converter as other answer suggested. But you could also create a new Class for your Path object, and define a FilePath string property in this class to store the full path. (And more properties if you need.)
Then you can just override the ToString method in this Class to return just the filename.
By doing this, you don't need to change the XAML, but the binding list will just display the item as the result returned by your override ToString method.
Something like:
public ObservableCollection<MyPath> fileNames { get; set; }
public class MyPath: ObservableObject //implement INotifyPropertyChanged
{
private string _filepath;
public string FilePath
{
get { return _filepath; }
set
{
if (value != _filepath)
{
_filepath= value;
RaisePropertyChanged("FilePath");
}
}
}
public override string ToString()
{
return System.IO.Path.GetFileName(FilePath);
}
}
<!-- View -->
<TextBox Text="{Binding str, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
// View Model
private string _str;
public string str
{
get { return _str; }
set
{
if (!value.Contains("a"))
_str = value;
OnPropertyChanged(nameof(str));
}
}
When typing in the TextBox I want it to throw out any invalid characters (in this sample case the letter 'a', but it could really be for anything). For example:
User types 'fds' followed by an 'a'
str detects a, so it doesn't set _str to 'fdsa', keeping it at 'fds' but raises the event anyway to indicate to the view to throw out the 'a'.
In WPF, this results in the textbox containing 'fds'. In UWP, this results in the textbox incorrectly containing 'fdsa' still.
It appears that in UWP when a control has focus, it will not respect the TwoWay binding.
I can create a button that has a Click event that when pressed will update my TextBox correctly.
private void btn_Click(object sender, RoutedEventArgs e)
{
OnPropertyChanged(nameof(str));
}
We have many ViewModels that we need to use in both WPF and UWP views, and we have this required behavior all over the place. What is a good solution to this problem?
* EDIT *
Came back to the problem after the weekend and it seems to have fixed itself. I have no idea why. I am closing the question for now.
You could use a converter to solve your problem, you could elaborate a better converter, in my example I just use a silly converter to demonstrate my idea.
Converter:
public class Converter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
var someString = value.ToString();
return someString.Replace("a", "");
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
XAML
<TextBox Text="{Binding Str, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource converter}}"/>
You could use an attached behavior also.
I'm trying to bind the background colour of my CardView to my view model, but I'm getting this error back from Mvx:
MvxBind:Warning: 11.66 Failed to create target binding for binding
CardBackgroundColor
I'm not sure whether I'm using the wrong property binding in the AXML or in the view model.
Here is the property I'm trying to bind it to in the view model:
public int EventEntryBackgroundColour
{
get
{
return IsRead
? Resource.Color.yellow
: Resource.Color.White;
}
}
I've also tried using it as a string type to return a colour in HEX, but it still doesn't work.
Here's the attribute, I'm setting on the CardView
cardview:MvxBind="CardBackgroundColor EventEntryBackgroundColour"
Any help with this would be much appreciated.
I faced the same issue. My solution...
Converter:
public class MessageStatusToColorDrawableConverter : MvxValueConverter<bool, ColorDrawable>
{
protected override ColorDrawable Convert(bool value, Type targetType, object parameter, CultureInfo cultureInfo)
{
var context = Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity; // To get the context of the activity
return value ? new ColorDrawable(new Color(ContextCompat.GetColor(context, Resource.Color.Pink))) : new ColorDrawable(new Color(ContextCompat.GetColor(context, Resource.Color.Green)));
}
}
XML:
<RelativeLayout
android:id="#+id/relay_archive"
android:layout_width="10dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:paddingLeft="10dp"
android:paddingRight="10dp"
local:MvxBind="Background MessageStatusToColorDrawable(Status)">
We can't bind an Android.Graphics.Color property to local:MvxBind="BackgroundColor... since the BackgroundColor property wants you to use the NativeColor converter which uses an MvxColor.
However, the local:MvxBind="Background... property wants a drawable, hence my use of binding a ColorDrawable.
You need to look into the Value converters of MvvmCross: https://github.com/MvvmCross/MvvmCross/wiki/Value-Converters#the-mvx-color-valueconverters
Using those you can set a color like this:
local:MvxBind="BackgroundColor NativeColor(CurrentColor)"
Or for iOS:
set.Bind(field)
.For(field => field.BackgroundColor)
.To(vm => vm.CurrentColor)
.WithConversion("NativeColor");
And Windows:
Fill="{Binding CurrentColor, Converter={StaticResource NativeColor}}"
I just defined a subclass of my cardview with a own property for it, since you cant directly access it else it seems and i didnt find a binding for it yet?
public class BindableColorCardView: CardView
{
private Color m_cCardViewColor;
public Color CardViewColor
{
get { return m_cCardViewColor; }
set
{
m_cCardViewColor = value;
SetCardBackgroundColor(m_cCardViewColor);
}
}
And then just used binded property with a ValueConverter.
I know this was asked a very long time ago but maybe it will help somebody out.
The answer now is that you need to use CardViewBackgroundColor instead of CardBackgroundColor.
So in your case that would be:
cardview:MvxBind="CardViewBackgroundColor EventEntryBackgroundColour"
You should bind it to a Android.Graphics.Color though and not an integer.
To do that, you can create a converter class like below:
public class BackgroundColorConverter : MvxValueConverter<bool, Color>
{
protected override Color Convert(bool value, Type targetType, object parameter, CultureInfo culture)
{
return value ? Color.Black : Color.White;
}
}
You can call this converter from the xaml too and pass a boolean from your ViewModel to display the correct color. Example:
cardview:MvxBind="CardViewBackgroundColor BackgroundColor(Selected)"
I have a custom textbox that has a dependencyProperty called valueProperty of type double nullable. My problem is that the property is binded to doubles nullables and no nullables on the model, and when I try to put a null and the binding value is no nullable, this obviously fails, showing the red rectangle. I want to detect the binding fail and assign a 0 when this occurs. So my question is: is there any way to detect that binding fail?
I know that I can fix it using 2 diferent customsTextbox for nullables and no nullables, and other ways, just wondering if there is a way to check the success of the binding. Thanks in advance.
EDIT >>>>>
Model:
private double _Temperature;
public double Temperature
{
get { return _Temperature; }
set { SetProperty(ref this._Temperature, value); }
}
private double? _Density;
public double? Density
{
get { return _Density; }
set { SetProperty(ref this._Density, value); }
}
View(simplified):
<local:customTextBox Value="{Binding Temperature}"/>
<local:customTextBox Value="{Binding Density}"/>
customTextBox dependencyProperty:
public static readonly DependencyProperty valueProperty =
DependencyProperty.RegisterAttached(
"Value",
typeof(double?),
typeof(customTextBox),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValuePropertyChanged)
);
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
customTextBox ctb = d as customTextBox;
ntb.Value = (double?)e.NewValue;
//Here I can check the binding fail.
}
EDIT WITH SOLUTION >>>>>
There was diferent solutions to my problem, I'll enumerate them:
#blindmeis solution. This is the easiest one, but the less potent to:
<local:customTextBox Value="{Binding Temperature, TargeNullValue=0}"/>
#Gary H solution. This is the one I selected as the solution, because it answer exactly what I was asking and also the easier to implement in my current app:
private static void OnValuePropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
customTextBox ctb = d as customTextBox;
ntb.Value = (double?)e.NewValue;
if (Validation.GetHasError(d))
{
//The binding have failed
}
}
#tomab solution. I think using converters is a good solution to(maybe better), but I still need keeping the customTextBox class because of other dependency properties, and I will need to refactor so much code. I will keep in mind that way for future implementations.
Thank you all for the help.
Yes this is a validation error.
You can query for the attached property:
var errors=Validation.GetErrors(myTextbox);
For handling and customizing validation see:
Data validation in WPF
Here is a way to use a Converter and its advantage is that you can control how you want the output value (which will be displayed) based on any logic you need.
public class DoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
// if you want to handle `double` too you can uncomment following lines but this is an ugly hack
// if (value != null && value.GetType() == typeof(double))
// {
// return value;
// }
var nullableDouble = (double?)value;
if (nullableDouble.HasValue)
{
return nullableDouble.Value;
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
And the xaml may look like this:
<UserControl.Resources>
<converters:DoubleConverter x:Key="DoubleConverter"/>
</UserControl.Resources>
<TextBox Text="{Binding SomeValue, Converter={StaticResource DoubleConverter}}" />
SomeValue must be of type double?.