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.
Related
I have a binding from a toggle button hooked up in code behind.
I want to bind the isChecked state from one button to 4 video controls to toggle the mute function. I am using multibinding to bind the toggle button to 4 different controls. My problem is using breakpoints i can see everything is triggered right up to the property mute property on each object, but the property "value" parameter never gets updated. It stays at the default setting when the controls are instantiated.
So first i create the bindings in code behind
IMultiValueConverter converter = new EmptyMultiValueConverter();
MultiBinding myMultiBinding = new MultiBinding();
myMultiBinding.Converter = converter;
myMultiBinding.UpdateSourceTrigger = UpdateSourceTrigger.Default;
myMultiBinding.Mode = BindingMode.OneWayToSource;
myMultiBinding.NotifyOnSourceUpdated = true;
for (int i = 1; i < _maxNumberofPlayers; i++)
{
VideoPlayer player = new VideoPlayer()
{
Mute = false
};
myMultiBinding.Bindings.Add(new Binding("Mute") { Source = player
});
}
btnMuteToggle.SetBinding(SimpleButton.IsCheckedProperty, myMultiBinding);
This all seems to work because when i click on the button i can see in the multivalue converter the correct isChecked button status arrives at the breakpoints, at ConvertBack below i can confirm the value is the correct bool that reflects the toggle button state.
public class EmptyMultiValueConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object
parameter, System.Globalization.CultureInfo culture)
{
// gets from the object source
return (bool)values[0];
}
public object[] ConvertBack(object value, Type[] targetTypes, object
parameter, System.Globalization.CultureInfo culture)
{
return new Object[] {value,value,value,value};
}
#endregion
}
At this point i can confirm it hits the Mute property, and triggers the SET 4 times, but as i trace through it the value parameter remains at the previously set value, and does not update to reflect the value passed to it via ConvertBack
// mute property in the media player user control
public bool Mute
{
get { return _media.IsMuted; }
set
{
if (_media.IsMuted == value)
return;
else
{
_media.IsMuted = value;
NotifyPropertyChanged("Mute");
}
}
}
Can anyone help please.
Have been tearing my hair out for 3 days.
It seemed to me using multibinding is an efficient way to hook up 4 seperat controls players and bind them to one button click.
well I have tried several options again and none of them work except code in a click button event
Multibinding just does not work at all.
I can see in break points that the Mute property is being called 4 times for 4 controls but the value parameter is never updated to the new property value.
I also tried binding the one button to 4 different controls
foreach(Player player in lsPlayers)
{
btnMuteToggle.SetBinding(SimpleButton.IsCheckedProperty, new Binding("Mute")
{
Source = player,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
Mode = BindingMode.OneWayToSource
});
}
This just results in one player object mute property being called.
You cant bind more than one control to the same button.
It just does not work.
so I just called the 4 objects in a click event.
I believe that in this case you may want to bind those 4 properties to the mute property individually and make sure each is set Mode to TwoWay. But if MultiValueConverter supports it, I can't particularly argue with your approach.
It appears that you have not made a Dependency Property for Mute. this is vital to the WPF Binding Workflow and tends to scale easier than NotifyPropertyChanged as NotifyPropertyChanged can tend towards requiring you to manage more. A DependencyProperty will abstract away both sending and receiving the updates to your property and the Xaml editor support for it is better.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/how-to-implement-a-dependency-property
I find WPF Binding to be nice in theory, but still requires a lot more leg work than it needs to. Have a function. I add it to all of my WPF viewmodels and controls.
/// <summary>
/// simplify wpf binding
/// </summary>
/// <param name="name"></param>
/// <param name="type"></param>
/// <param name="preferNull">Fair Warning. if you create an instance, you will have also created a singleton of this property for all instances of this class. </param>
/// <returns></returns>
private static DependencyProperty AddDp(string name, Type type, bool preferNull = true)
{
return DependencyProperty.Register(name, type, typeof(Setting),
new PropertyMetadata((!preferNull || type.IsValueType) ? Activator.CreateInstance(type) : null));
}
Just inherit from DependencyObject and you can make mute look more like this:
public static readonly DependencyProperty MuteDp = AddDp(nameof(Mute), typeof(bool));
public bool Mute
{
get => (bool)GetValue(MuteDp);
set { if(value != Mute) SetValue(MuteDp, value); }
}
But Be Warned!
Sometimes WPF Binding to a Dependency Property updates the Dependency Property's internal values without entering the setter of its corresponding property! This was the case on a MultiValueBinding I used.
meaning it may never touch the Mute::Set accessor. This can be pretty problematic, to counter this issue you can use the various callbacks available to a DependencyObject!
private static DependencyProperty AddDp(string name, Type type, bool preferNull = true)
{
var dp = DependencyProperty.Register(name, type, typeof(Setting),
new PropertyMetadata(
(!preferNull || type.IsValueType) ? Activator.CreateInstance(type) : null
,new PropertyChangedCallback((dobj, dpe)=>
{
//after property changed.
//forcing usage of set accessor
((MuteContainerObj)dobj).Value = ((strong text)dobj).Value;
//or forcibly use an update function
((MuteContainerObj)dobj).MuteUpdated();
}),new CoerceValueCallback((dobj, o)=> {
//before property changed
return o;
})), new ValidateValueCallback((o)=> {
//before property change events
return true;
}));
return dp;
}
This question already has an answer here:
Databindings don't seem to refresh
(1 answer)
Closed 5 years ago.
In my UWP app I have a TextBlock, which should display a (formatted) date, that is bound to a property in the view model:
<TextBlock Style="{StaticResource summaryTextStyleHighlight}" Margin="0,10,0,0"
Text="{x:Bind ViewModel.CurrentDisplayDay, Converter={StaticResource DateFormatConverter}, ConverterParameter=d, Mode=OneWay}"
Name="lblCurrentDate" />
The converter is "configured" in the XAML like this:
<local:DateFormatConverter x:Key="DateFormatConverter" />
And the converter class is as followed:
public class DateFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
DateTime dt = DateTime.Parse(value.ToString());
if (parameter != null)
{
return dt.ToString((string)parameter, Utils.GetCurrentCultureInfo());
}
return dt.ToString("g", Utils.GetCurrentCultureInfo());
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
The ViewModel has a simple property for the DateTime value that is bound:
public DateTime CurrentDisplayDay
{
get;
private set;
}
But when I update the value in the ViewModel, the value won't get updated in the TextBlock on the (Main)Page.
I tried to move the property to the page, but that didn't help. If I refresh the page (navigate to it again), then the updated value is being displayed but I don't want to navigate to it, it should show the updated value through the binding.
What could be the issue?
#Patric You seem to be doing almost everything correctly, but you have forgotten about one step.
Is there any notification indicating that your property has been updated, when its value changes? You need to propagate a notification to the UI indicating that your ViewModel property has been altered, because otherwise even though the Text Dependency property is actively listening for any notification from the "source" (you have defined the binding as One-Way), you are simply not communicating anything to it.
Your ViewModel should implement the INotifyPropertyChanged Interface, which exposes the PropertyChanged event.
The property changed event will be responsible for communicating the update.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
On your property setter, you simply have to invoke this method, which will consequently be responsible for invoking the PropertyChanged event, with the appropriate event data.
Edit:
In order to use the CallerMemberName Attribute (which allows you to get the name of the property which called the method) use the following namespace System.Runtime.CompilerServices
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).
<!-- 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)"