Button will not work, unable to find suitable setter or getter - avaloniaui

public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
Queue = new QueuePanelViewModel();
Merge = new MergePanelViewModel();
CurrentQueuePanel ??= new QueuePanel();
CurrentMergePanel ??= new MergePanel();
_selectedView = CurrentQueuePanel;
}
public QueuePanelViewModel Queue { get; }
public MergePanelViewModel Merge { get; }
private UserControl _selectedView;
public UserControl SelectedView
{
get
{
return _selectedView;
}
set
{
_selectedView = value;
}
}
private static QueuePanel CurrentQueuePanel { get; set; }
private static MergePanel CurrentMergePanel { get; set; }
private void OnPanelButtonClickHandler(object sender, RoutedEventArgs e)
{
switch (((Button)sender).Tag)
{
case "Queue":
SelectedView = CurrentQueuePanel;
break;
case "Merge":
SelectedView = CurrentMergePanel;
break;
default:
((Button)sender).Content = "Somethin went wrong...";
break;
}
e.Handled = true;
}
}
And in the .axaml
<Button Tag="Queue" Click="{Binding OnPanelButtonClickHandler}" ClickMode="Press" Margin="0" Grid.Row="0" Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="Queue" Classes="btn" />
The button event will not work in any fashion I have tried. In this attempt It gives me the exception
'Unable to find suitable setter or adder for property Click of type Avalonia.Controls:Avalonia.Controls.Button for argument Avalonia.Markup:Avalonia.Data.Binding, available setter parameter lists are:
System.EventHandler`1[[Avalonia.Interactivity.RoutedEventArgs, Avalonia.Interactivity, Version=0.10.12.0, Culture=neutral, PublicKeyToken=c8d484a7012f9a8b]] Line 40, position 26.' Line number '40' and line position '26'.
If I use a Command instead of Click, it will compile however the button becomes disabled.

You are getting this exception because Click is the RoutedEvent and OnPanelButtonClickHandler should be in the *.axaml.cs code behind.
If you want to call the function in your view model from the view you should use Command property and bind to the function or implement a command in your view model.
In your case the button is inactive when you bind to the command because you do not pass the required parameters. This should work:
private void OnPanelButtonClickHandler(string parameter)
<Button Command="{Binding OnPanelButtonClickHandler}" CommandParameter="Queue" .../>
You can find more information in the docs

Related

Xamarin FreshMvvm Simple Binding

I have a simple question. How do I access an entry value from a .xaml file in the pagemodel with the FreshMvvm framework.I want the default value that's set in the settings.cs constructor to be what the user inputs in the entry field.
Thanks!
testpage.xaml:
<Label Text="Set Server Address (FreshMvvm Binding):" />
<Entry Text="{Binding Settings.SyncServiceAddress}" Placeholder="Server IP Address" />
<Button Text="Sync Web Service - (FreshMvvm Binding)" Command="{Binding SyncButtonFreshMvvmBinding_Clicked}" />
testpagemodel.cs:
public override void Init(object initData)
{
if (initData != null)
{
Settings = (Settings)initData;
}
else
{
Settings = new Settings();
}
}
public Command SyncButtonFreshMvvmBinding_Clicked
{
get
{
return new Command(async () =>
{
string serverAddress = Settings.SyncServiceAddress;
SyncService.PullNewXMLData(serverAddress);
await CoreMethods.PushPageModel<DashboardPageModel>();
});
}
}
settings.cs:
public class Settings : ObservableObject
{
// Constructor
public Settings()
{
// Default value
SyncServiceAddress = "http://localhost/psm/service.aspx";
}
// Properties
public string SyncServiceAddress { get; set; }
public string UserIDSettings { get; set; }
}
Keeping in mind that the UI only needs to know about a limited set of properties, its ok to create a bindable property in the ViewModel so you're not trying to bind to a nested property.
Create a new string property in the ViewModel called SyncServiceAddress;
public string SyncServiceAddress
{
get{
return Settings.SyncServiceAddress;
}
set{
Settings.SyncServiceAddress = value;
}
}
Then update your XAML to this.
<Entry Text="{Binding SyncServiceAddress}" Placeholder="Server IP Address" />
This should resolve your issue.

Curious difference in behavior between compiled and regular binding

I'm trying to create a MenuFlyout with ToggleMenuFlyoutItems where one and only one toggle is checked at any given moment. The toggles corresponds to ToggleViewModels, binding the IsChecked property of the toggle to an IsSelected property of the ToggleViewModel. Because I want to uncheck the previously checked toggle whenever a new toggle is checked I relay the setting of the IsSelected property to the MainViewModel that holds the collection of ToggleViewModels.
Button with flyout defined in MainPage.xaml
<Button Content="Asdf">
<Button.Flyout>
<MenuFlyout>
<ToggleMenuFlyoutItem
Text="{x:Bind _viewModel.ToggleCollection[0].Name}"
IsChecked="{x:Bind _viewModel.ToggleCollection[0].IsSelected, Mode=TwoWay}" />
<ToggleMenuFlyoutItem
Text="{x:Bind _viewModel.ToggleCollection[1].Name}"
IsChecked="{x:Bind _viewModel.ToggleCollection[1].IsSelected, Mode=TwoWay}" />
<ToggleMenuFlyoutItem
Text="{x:Bind _viewModel.ToggleCollection[2].Name}"
IsChecked="{x:Bind _viewModel.ToggleCollection[2].IsSelected, Mode=TwoWay}" />
</MenuFlyout>
</Button.Flyout>
</Button>
MainPageViewModel:
public class MainViewModel : BindableBase
{
public MainViewModel()
{
ToggleCollection = new ObservableCollection<ToggleViewModel>();
var selectToggleAction = new Action<ToggleViewModel>(param => SetToggleSelection(param));
for (int i = 0; i < 3; i++)
{
ToggleCollection.Add(new ToggleViewModel($"Item {i}", selectToggleAction));
}
}
public ObservableCollection<ToggleViewModel> ToggleCollection { get; private set; }
private void SetToggleSelection(ToggleViewModel toggle)
{
var selectedToggle = ToggleCollection.SingleOrDefault(t => t.IsSelected);
if (selectedToggle != toggle)
{
selectedToggle?.SetSelection(false);
toggle.SetSelection(true);
}
}
}
ToggleViewModel:
public class ToggleViewModel : BindableBase
{
private Action<ToggleViewModel> _selectToggleAction;
private bool _isSelected;
public ToggleViewModel(string name, Action<ToggleViewModel> action)
{
Name = name;
_selectToggleAction = action;
}
public string Name { get; set; }
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_selectToggleAction(this);
base.OnPropertyChanged();
}
}
}
public void SetSelection(bool selected)
{
_isSelected = selected;
base.OnPropertyChanged("IsSelected");
}
}
Now all the code above works very well. The problem occurs when I try to use regular bindings instead of compiled ones:
<ToggleMenuFlyoutItem
Text="{Binding ToggleCollection[0].Name}"
IsChecked="{Binding ToggleCollection[0].IsSelected, Mode=TwoWay}" />
Binding the properties like this I'm suddenly able to uncheck the currently checked toggle so that none is selected. This is due to the getter of the IsSelected property not being called when I raise the OnPropertyChanged in the setter of the IsSelected property (the reason for using regular bindings is that I want to create the toggles dynamically in code behind, but to illustrate the problem XAML works just as well).
Can anyone explain to me why the {x:Bind} in this case works but not the {Binding}?

XAML Binding - App.cs

Question about binding in XAML with WP8.
In my App.cs I declare a public property for class Setting. In other xaml pages I need to access that propery and pass that property to a ConverterParameter. I can't say I've found a clean way of doing this. Below is my current method of how I accomplish this, but it just feels dirty. Any other ways out there?
So what's happening with code below? In app the settings data gets loaded. Any time the settings gets loaded or the a setting changes it Removes/Adds App.Current.Resource. This then allows me to data bind it {StaticResource {resourceName}}
Again, this works 100%...but is there a better/another way to accomplish this?
App.cs
private static Settings _settings = null;
public static Settings Settings
{
get { return _settings; }
private set { _settings = value; }
}
private async void Application_Launching(object sender, LaunchingEventArgs e)
{
if (Settings == null)
Settings = await FlightPath.Core.Data.LoadSettingsAsync();
App.Current.Resources.Add("Settings", App.Settings);
Settings.SettingsChanged += Settings_SettingsChanged;
}
private void Settings_SettingsChanged(object sender, EventArgs e)
{
if (App.Current.Resources["Settings"] == null)
App.Current.Resources.Add("Settings", App.Settings);
else
{
App.Current.Resources.Remove("Settings");
App.Current.Resources.Add("Settings", App.Settings);
}
}
Application Page XAML using Converter / ConverterParameter
<TextBlock Text="{Binding observation_time,
Converter={StaticResource ZuluToLocalTimeConverter},
ConverterParameter={StaticResource Settings}}"
Style="{StaticResource PhoneTextNormalStyle}"
Margin="-4,0,0,0"/>
if you are using MVVM you can Create a SettingManager class which having a Singleton instance. Then declare its propert in ViewModelBase class. Finally use it into your xaml code
XAML
C#
class ViewModelBaseClass: InotifyPropertyChanged
{
public SettingManager Settings{get{return SettingManager.Instance;}}
}
class SettingManager
{
public static Instance{get{...}}
public string this[string sName]
{
return "whatever you need";
}
}
class MYViewModel: ViewModelBase
{
}

Have complex object update in view when property changed - property changed event not fired - WinRT/XAML

I have a WinRT app with a number of Users, Projects, Meetings, etc.
I have a main screen, with a main screen view model, which should display CurrentUser and has a ListView bound to CurrentUser.ProjectList.
I initialise CurrentUser in the ViewModel using a UserProvider class that gets all the required information from the database.
My problem then becomes very similar to this: Subscribe to INotifyPropertyChanged for nested (child) objects
I have a user and project model:
public class User
{
public int id { get; set; }
public string ForeName { get; set; }
public string Surname { get; set; }
... etc ...
public ObservableCollection<Project> ProjectList { get; set; }
public ObservableCollection<User> FriendList { get; set; }
... constructor
}
public class Project
{
public String Name { get; set; }
public int Id { get; set; }
public List<User> Users { get; set; }
public List<Meeting> Meetings { get; set; }
.. constructor ...
}
A view model with the following:
class HomeScreenViewModel : INotifyPropertyChanged {
private User _currentUser;
public User CurrentUser
{
get { return this._currentUser; }
set
{
if (Equals(_currentUser, value)) return;
this._currentUser = value;
RaisePropertyChanged("CurrentUser");
}
}
//[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
... I have a method in this view model that gets the current user
public async Task<bool> GetLoggedInUserAsync()
{
int testId = 0;
CurrentUser = await userProvider.GetCurrentUser(testId);
UserProjects = await userProvider.GetUsersProject(CurrentUser);
CurrentUser.ProjectList = UserProjects;
return true;
}
That is called in the view's loadState
public MainPage()
{
this.InitializeComponent();
addMeeting = new AddMeetingFlyout();
_vm = new HomeScreenViewModel();
this.DataContext = _vm;
}
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
await _vm.GetLoggedInUserAsync()
}
And my bindings in the XAML, for ProjectList and ForeName, for example, are as follows:
<CollectionViewSource
x:Name="projectsViewSource"
Source="{Binding CurrentUser.ProjectList}"/>
...
<ListView
x:Name="projectList"
ItemsSource="{Binding Source={StaticResource projectsViewSource}}"
Grid.Row="1"
SelectionMode="None"
Style="{StaticResource DraggableListView}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
IsItemClickEnabled="True"
>
<ListView.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource ProjectTileButton}" Content="{Binding Name}" Click="ProjectItem_Click" />
</DataTemplate>
</ListView.ItemTemplate>
<AddDeleteThemeTransition/>
</ListView>
...
<Button ...>
<TextBlock ...">
<Run Text="{Binding CurrentUser.ForeName}" />
</TextBlock>
</Button>
The button content, CurrentUser.ForeName fires an INotifyPropertyChanged event when CurrentUser is first initialised in the viewmodel. This is reflected in the view - but any further changes to CurrentUser.ForeName do not fire any subsequent INotifyPropertyChanged events. The ProjectList is also not displayed in the view and does not fire an INotifyPropertyChanged event even though I know it is there.
I have spent many days looking at implementing INotifyPropertyChanged so that changes to nested child complex objects (such as CurrentUser.ProjectList) will propagate up to the view. At the minute, the only way this happens is if I force a call to
this._currentUser = value;
RaisePropertyChanged("CurrentUser");
which I am testing with a button that calls a method called MakeChange() in the viewmodel
public void MakeChange()
{
User updatedCurrentUser = CurrentUser;
CurrentUser = updatedCurrentUser;
}
This works, so I know for a fact all the data is coming correctly from the database and all is as it should be - one less thing to worry about!
However, I simply cannot get the view to display user projects on page load, or when new projects are added.
I tried implementing this solution: https://gist.github.com/thojaw/705450, however, the WinRT reflection capabilites have changed and I am not sure how to get the following liens to work within the context of my project, as this is beyond me:
//from property
//in _type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
//where _inotifyType.IsAssignableFrom(property.PropertyType)
//select property;
Any help would be greatly appreciated - I honestly thought all I had to do was bind CurrentUser.ProjectList to a ListView.
As you are replacing the entire ObservableCollection itself, then you will also need to introduce another property changed event and backing field for the collection property.
There is a good example of this here

XamlParseException Failed to assign to property. Binding not working with attached property

I want to create custom text box with attached property for Windows Store app. I am following this solution. Now it uses hard coded value as property value but I want to set value using binding, but it's not working. I tried to search a lot but didn't helped me any solution.
The exception details is like this
An exception of type 'Windows.UI.Xaml.Markup.XamlParseException'
occurred in CustomTextBox.exe but was not handled in user code
WinRT information: Failed to assign to property
'CustomTextBox.Input.Type'.
MainPage.xaml
<!-- local:Input.Type="Email" works -->
<!-- local:Input.Type="{Binding SelectedTextboxInputType}" not working -->
<TextBox x:Name="txt" local:Input.Type="{Binding SelectedTextboxInputType}" Height="30" Width="1000" />
<ComboBox x:Name="cmb" ItemsSource="{Binding TextboxInputTypeList}" SelectedItem="{Binding SelectedTextboxInputType}" Height="30" Width="200"
Margin="451,211,715,527" />
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new ViewModel();
}
}
Input.cs
//InputType is enum
public static InputType GetType(DependencyObject obj)
{
return (InputType)obj.GetValue(TypeProperty);
}
public static void SetType(DependencyObject obj, InputType value)
{
obj.SetValue(TypeProperty, value);
}
public static readonly DependencyProperty TypeProperty =
DependencyProperty.RegisterAttached("Type", typeof(InputType), typeof(TextBox), new PropertyMetadata(default(InputType), OnTypeChanged));
private static void OnTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is InputType)
{
var textBox = (TextBox)d;
var Type = (InputType)e.NewValue;
if (Type == InputType.Email || Type == InputType.URL)
{
textBox.LostFocus += OnLostFocus;
}
else
{
textBox.TextChanged += OnTextChanged;
}
}
}
ViewModel.cs
public class ViewModel : BindableBase
{
public ViewModel()
{
TextboxInputTypeList = Enum.GetValues(typeof(InputType)).Cast<InputType>();
}
private InputType _SelectedTextboxInputType = InputType.Currency;
public InputType SelectedTextboxInputType
{
get { return _SelectedTextboxInputType; }
set { this.SetProperty(ref this._SelectedTextboxInputType, value); }
}
private IEnumerable<InputType> _TextboxInputTypeList;
public IEnumerable<InputType> TextboxInputTypeList
{
get { return _TextboxInputTypeList; }
set { this.SetProperty(ref this._TextboxInputTypeList, value); }
}
}
This is a pretty common mistake. The problem is, binding targets cannot be CLR properties in XAML. It's just the rules. A binding source can be a CLR property, just fine. The targets simply must be dependency properties.
We all get the error! :)
I describe the whole thing here: http://blogs.msdn.com/b/jerrynixon/archive/2013/07/02/walkthrough-two-way-binding-inside-a-xaml-user-control.aspx
Best of luck.
Incorrect
public static readonly DependencyProperty TypeProperty =
DependencyProperty.RegisterAttached("Type", typeof(InputType), typeof(TextBox), new PropertyMetadata(default(InputType), OnTypeChanged));
Correct
public static readonly DependencyProperty TypeProperty =
DependencyProperty.RegisterAttached("Type", typeof(InputType), typeof(Input), new PropertyMetadata(default(InputType), OnTypeChanged));