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)"
Related
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.
I have this Class:
public class MyData
{
public static int Total Files;
public static int Total FilesFinished;
}
And I have simple Progress-Bar that calculate its Value this way:
double value = ((double)MyData.FilesFinished / MyData.Files) * 100;
And update my Label using simple Timer:
Label name="lblPercentage" />
lblPercentage.Content = value;
Now I want to use Converter instead of updating my Label via code behind.
So I have this class (not implemented yet):
public class TotalFilesToTotalPercentageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Inside my Window.Resource I have this:
<Convertors:TotalFilesToTotalPercentageConverter x:Key="FilesToPercentageConverter "/>
And this is what I have try inside my Label:
Content="{Binding Converter={StaticResource FilesToPercentageConverter}}"
So my problem is that I try to see if my TotalFilesToTotalPercentageConverter class is responding via the debugger and it seems not, nothing happening.
What did I do wrong?
Update
I forget to mention that my TotalFilesToTotalPercentageConverter class in inside Converter folder under Utils folder under Classes folder
You need to bind the Content property to a source property for your Convert method to be invoked. Converters only work with data bindings.
This means that instead of setting the Content property of the Label in the code-behind like this:
lblPercentage.Content = value;
You should set a source property of a view model that you then bind the Content property of the Label to:
Content="{Binding Path=YourValueProperty, Converter={StaticResource FilesToPercentageConverter}}"
Set the DataContext of your view to an instance of your view model class:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
The view model class needs to implement the INotifyPropertyChanged and raise the PropertyChanged event in the setter of the source property (YourValueProperty).
I am not sure if I am going in the right direction.
I wanted to reuse some part of the page on different pages of my application so I developed those pages on user control wpf xaml..
Can you please help or suggest some example on how to integrate different xaml pages to mainWindow.xaml so as to run my application.
so I developed those pages on user control wpf xaml
Here is a design philosophy, place all those user controls on the page in a common frame/location, then in the VM create an enum which will define the different states of the page which directly relate to each control.
Bind all the controls visibility to a property on the VM which identifies the current state (from the aforementioned state enum) and then using a converter hide or show the control.
Here is an example, place the enum on the VM class.
public enum Operations
{
Authentication = -1,
DisplayResults = 0,
EditData,
...
}
Create a property which will define the current state on the VM
public Operations CurrentState
{
get { return _CurrentState; }
set { _CurrentState = value; PropertyChanged(); }
}
Then on the control bind to that state, but also pass in text to identify the control
<controls:Authentication
Visibility="{Binding CurrentState,
ConverterParameter=Authentication,
Converter={StaticResource StateToVisiblity}}"/>
Then in our converter reference the enums and based on the current state determine whether the control should be shown or not:
public class OperationStateToVisiblity : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value != null) &&
(parameter != null) &&
value.ToString().Equals(parameter.ToString())
? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
That way each control only is shown when the state changes and that control is defined to a specific enum.
<!-- 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 want to set an image's source according to its DataContext in a ChildWindow. Here is the XAML file:
<controls:ChildWindow x:Class="CEM.Controls.DialogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" Title="{Binding Title}">
...
<Image x:Name="DialogIcon"></Image>
...
</controls:ChildWindow>
It's working fine if I override the Show method of the ChildWindow and set the image's source:
public new void Show()
{
DialogIcon.Source = new BitmapImage(new Uri(#"/Images/DialogWindow/Confirm.png", UriKind.Relative));
base.Show();
}
But it looks ugly and it's not the "silverlight way", so I decide to change:
<Image x:Name="DialogIcon" Source="{Binding DialogIconType, Converter={StaticResource DialogIconConverter}}"></Image>
You see I have a DialogIconConverter registered to bind the source from the DataContext.
public class DialogIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//currently it's an hard-coded path
return new BitmapImage(new Uri(#"/Images/DialogWindow/Confirm.png", UriKind.Relative));
}
...
}
But it's not working now, I have several other converters in this control which are working fine. Only this one is not working. Can you help to find where the problem is?
EDIT: DialogIconType is an enum, and also it's a property of DialogContext. An instance of DialogContext will be assigned to DataContext property of the DialogWindow.
public enum DialogIconType
{
Confirm,
Alert,
Error
}
public class DialogContext
{
public string Title { get; set; }
public string Content { get; set; }
public DialogButtons Buttons { get; set; }
public DialogIconType IconType { get; set; }
}
internal DialogWindow(DialogContext context)
{
InitializeComponent();
this.DataContext = context;
}
might be silly, but did you make sure that your converter is referenced properly in your xaml file ?
otherwise, I suggest trying this syntax as path for your URI (with images setup as resources):
return new BitmapImage(new Uri("pack://application:,,,/Images/DialogWindow/Confirm.png", UriKind.Relative));
EDIT :
ok, I think I've got it :
look into your output window, you will probably see some error 40 binding ... blablabla...
My guess is that the converter is right, but the source of the binding isn't, so basically the converter is not even used.
The reason is that your DialogIconType is not a dependency property, so it cannot be bound to.
in other words, this :
public DialogIconType IconType { get; set; }
should become this :
public static DependencyProperty IconTypeProperty = DependencyProperty.Register("IconType", typeof(DialogIconType), typeof(DialogContext));
public DialogIconType IconType
{
get { return (DialogIconType)(GetValue(IconTypeProperty)); }
set { SetValue(IconTypeProperty , value); }
}
plus, in your Xaml, you should Bind to "IconType", and not "DialogIconType" (which is a type and not a property)
(this might even be the sole issue, as I'm not sure if a dependencyProperty Is actually realy needed here, now that I think of it)
Assuming that DialogIconType is the path to your image (e.g. "Images/DialogWindow/Confirm.png"), it should work without a valueconverter as shown below:
<Image Source="{Binding DialogIconType}" />
EDIT:
Returning the path to the image from the valueconverter's Convert method is also possible - i.e.:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return "Images/DialogWindow/Confirm.png";
}
EDIT 2:
The following also works using UriKind.Relative:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new BitmapImage(new Uri("Images/DialogWindow/Confirm.png", UriKind.Relative));
}