I've faced a problem: Attached property doesn't work for Button's Flayout.
I have an outer button with inner button (flayout), attached property - is a ICommand type property. Outer button successfully binds to attached property BUT inner button doesn't.
Here User Control code:
<UserControl x:Class="uwp_AttachedProperty.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:uwp_AttachedProperty"
Name="Root">
<Button Command="{Binding ElementName=Root, Path=(local:AttachedProps.CommandAdd), Mode=OneWay}">
<Button.Flyout>
<Flyout>
<!--*** DOESN'T WORK HERE***-->
<Button Command="{Binding ElementName=Root, Path=(local:AttachedProps.CommandAdd),Mode=OneWay}"/>
</Flyout>
</Button.Flyout>
</Button>
</UserControl>
Attached property code:
public sealed class AttachedProps : DependencyObject
{
public static readonly DependencyProperty CommandAddProperty = DependencyProperty.Register(
"CommandAdd",
typeof(ICommand),
typeof(AttachedProps),
new PropertyMetadata(null));
public static void SetCommandAdd(UIElement element, ICommand value) { element.SetValue(CommandAddProperty, value); }
public static ICommand GetCommandAdd(UIElement element) { return (ICommand)element.GetValue(CommandAddProperty); }
}
My case is an ICommand propery, but it doesn't work any type (double, string etc.) of attached property.
Does any one faced the same problem, how can it be fixed?
(Windows 10, 1809, Build: 17763)
Thanks.
Attached Property binding not working for Flayout (Xaml, UWP)
Derive this case reply The problem is you used Binding ElementName=Root in the Flyout, When you bind data in the content of Flyout, the binding source is in Page, but the actual binding target is in PopupRoot, they have different DataContext, so can't it work here.
Related
I am new to MAUI and I have a working project that uses an sqlite database to store my data for my project.
I am trying to inject my database access object into the ViewModel for one of my Content Pages.
I had it working previously by just creating ("new'ing up") my database access object and the database and project worked fine.
When I changed this so that I would inject my database access object into the ViewModel's constructor I get an error:
/Users/RemoteCommand/Projects/Notes/Views/AllNotesPage.xaml(9,9): Error: XLS0507: Type 'AllNotes' is not usable as an object element because it is not public or does not define a public parameterless constructor or a type converter. (Notes) IntelliSense
Here is my XAML file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:Notes.Models"
x:Class="Notes.Views.AllNotesPage"
Title="AllNotesPage">
<ContentPage.BindingContext>
<models:AllNotes />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<!--<ToolbarItem Text="Add" Clicked="Add_Clicked" IconImageSource="{FontImage Glyph='+', Color=White, Size=22}"/>-->
<ToolbarItem Text="Add" Command="{Binding AddClickedCommand}" IconImageSource="{FontImage Glyph='+', Color=White, Size=22}"/>
</ContentPage.ToolbarItems>
<CollectionView x:Name="notesCollection"
ItemsSource="{Binding Notes}"
Margin="20"
SelectionMode="Single"
SelectionChanged="notesCollection_SelectionChanged">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="10"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Text}" FontSize = "22"/>
<Label Text="{Binding Date}" FontSize="14" TextColor="Silver"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
Here is my code behind:
namespace Notes.Views;
using Notes.Models;
using Notes.Views;
using Notes.Data;
using CommunityToolkit.Mvvm.Input;
public partial class AllNotesPage : ContentPage
{
public AllNotesPage()
{
InitializeComponent();
}
async void notesCollection_SelectionChanged(System.Object sender, Microsoft.Maui.Controls.SelectionChangedEventArgs e)
{
if(e.CurrentSelection.Count != 0)
{
var note = (Note)e.CurrentSelection[0];
await Shell.Current.GoToAsync($"{nameof(NotePage)}?{nameof(NotePage.ItemId)}={note.ID}");
}
}
}
Here is my ViewModel:
using System;
using System.Collections.ObjectModel;
using Notes.Data;
using Notes.Views;
using CommunityToolkit.Mvvm;
using CommunityToolkit.Mvvm.Input;
namespace Notes.Models;
public partial class AllNotes
{
NotesDatabase _notesDatabase;
public ObservableCollection<Note> Notes { get; set; } = new ObservableCollection<Note>();
public AllNotes(NotesDatabase notesDatabase)
{
_notesDatabase = notesDatabase;
LoadNotes();
}
[RelayCommand]
async void AddClicked()
{
await Shell.Current.GoToAsync(nameof(NotePage));
}
public async void LoadNotes()
{
Notes.Clear();
List<Note> notes = await _notesDatabase.GetItemsAsync();
foreach(Note note in notes)
{
Notes.Add(note);
}
}
}
and here is my MauiProgram where I define the dependency injection:
using Microsoft.Extensions.Logging;
using Notes.Views;
using Notes.Models;
using Notes.Data;
namespace Notes;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
builder.Services.AddSingleton<AllNotes>();
builder.Services.AddSingleton<AllNotesPage>();
builder.Services.AddSingleton<NotesDatabase>();
return builder.Build();
}
}
[Note: I have switched the AddSingleton to Add Transient for this page for these definitions to see if that would fix the problem but it did not]
I've tried a really basic dependency injection on an earlier test project where I injected my data access object into the code behind and I got the same error about missing a parameterless constructor and it turns out what I was missing was defining my data access object AND ContentPage as a Transient or Singleton in MauiProgram (Which is why for this project I added the data access object, ContentPage, and ViewModel as Singletons to the MauiProgram). Once I did that it worked, but now that I am using a ViewModel that I bind in the XAML I can't seem to get DI working for the ViewModel.
Please help this greenhorn!
Sincerely,
Doing this in XAML is an open issue: Resolve XAML BindingContext from ServiceCollection.
For now, do this via code behind's constructor, with a parameter:
public AllNotesPage(AllNotes vm)
{
InitializeComponent();
BindingContext = vm;
}
DI will inject vm, doing the needed instantiation of AllNotes and its NotesDatabase.
I have an ImageButton that is not firing Command binding command using VMMV architecture. First all other bindings are working properly in the view.
Here is button:
<ImageButton Command="{Binding SelectedItemCommand}" Source="{Binding Logo}" Grid.Row="0" Grid.Column="1" HeightRequest="150" WidthRequest="150" HorizontalOptions="CenterAndExpand" VerticalOptions="EndAndExpand"></ImageButton>
and in ViewModel:
public ICommand SelectedItemCommand => new Command(GetSelectedItem);
When I click the image nothing happens. I've even tried to bind to Pressed parameter but from everything I have read only the Command parameter should be used in a binding scenario. Putting a breakpoint on the function GetSelectedItem never gets reached.
What am I doing wrong here?
Sorry been away for a few days. So nothing was working on the suggestions even though they really should be clicking wouldn't fire command. Anyway I managed to get it to fire now using an eventhandler like so:
SelectedItemCommand = new Command<string>(param => OnItemSelected(param));
public void OnItemSelected(string img1_2)
{
PressedEventHandler?.Invoke(this, EventArgs.Empty);
}
The param captures the CommandParameter so I know which image to the question was clicked "img1" "img2" to do something specific. So my function now accepts a sender object and empty eventarg. I would like to instead pass img1_2 value but that doesn't appear to be possible as of now. What is cusrious is the sender object contains all the properties and values from the images (like an array of all my properties) but I cannot seem to get at them.
Attempted this:
string str = Item1Image.ToString(); // property in sender and viewmodel
But this returns a null value and not value listed in the sender object value?
Any additional thoughts?
TIA!
Rick...
public ICommand SelectedItemCommand {get; private set;}
...
public YourViewModel(){
...
SelectedItemCommand = new Command(GetSelectedItem);
...
}
...
Or
public ICommand SelectedItemCommand{
get
{
return new Command(() => {
//Do something
});
}
}
I have been using the code layout here to create an SVG container for a Xamarin Forms project. It works well and I reconfigured it to read the image from a remote URL.
The ResourceId is from the source above:
public static readonly BindableProperty SvgUriProperty = BindableProperty.Create(
"ResourceId",
typeof(string),
typeof(SvgUriContainer),
default(string),
propertyChanged: RedrawCanvas);
public string ResourceId
{
get => (string)GetValue(SvgUriProperty);
set => SetValue(SvgUriProperty, value);
}
However I can not seem to bind that URL at run time in the XAML:
<control:SvgUriContainer
Grid.Row="0"
Grid.RowSpan="4"
Grid.Column="0"
ResourceId="{Binding StampUri}"
[...]
/>
That Binding returns a string and the rest of the bindings work fine. Any attempt to bind in that fashion results in a build error:
No property, bindable property, or event found for 'ResourceId', or mismatching type between value and property.
No matter what I do in the container logic with the BindablePropertycreation, the error is the same and is in the XAML. Am I getting the syntax wrong in the XAML? Is it because Binding is a BindingExtension (not a string) at build time?
Note: if I replace the Binding with a string/URL it all works fine.
Note: if I set the type of ResourceId to object I can get by the build error but the string does not resolve, of course.
Can you try to modify your bindableProperty to the standard format described in document:
public static readonly BindableProperty SvgUriProperty = BindableProperty.Create(
"SvgUri",
typeof(string),
typeof(EventToCommandBehavior),
"test",
propertyChanged: RedrawCanvas);
public string SvgUri
{
get => (string)GetValue(SvgUriProperty);
set => SetValue(SvgUriProperty, value);
}
Xamarin requires a Property naming convention.
I have a custom control based on ContentView with some BindableProperty fields. These controls work great on normal ContentPage pages.
I am now trying to use them in a popup, using the Rg.Plugins.Popup nuget package. The controls display normally, but they never show any values - the binding does not appear to be working. The fields do appear to get bound to null before the constructor of the popups ContentView is called, but nothing happens when the BindingContext is changed.
Here is how the BindableProperty(s) are set up in the control:
public string FieldValue { get; set; }
public static readonly BindableProperty FieldValueProperty = BindableProperty.Create(
propertyName: "FieldValue",
returnType: typeof(string),
declaringType: typeof(MFGField),
defaultValue: "",
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: FieldValuePropertyChanged);
private static void FieldValuePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (MFGField)bindable;
if(newValue != null)
control.ValueEntry.Text = newValue.ToString();
}
Here is how it is set up in the XAML:
<MyNamespace:MFGField x:Name="OrderNumber" FieldValue="{Binding CurrentPick[Order_Number]}" LabelText="Order Number:" ReadOnly="true" Grid.Column="0" Grid.Row="0"/>
The popup ContentView currently has a ViewLifecycleEffect that serves as the 'OnAppearing' for the popup, and that is currently where I am setting the BindingContext after loading the data:
private async void ViewLifecycleEffect_OnLoaded(object sender, EventArgs e)
{
await callingPage.LoadBinRecord(callingPage.SearchTextValue);
((AppData)this.BindingContext).CurrentPick = ((AppData)this.BindingContext).CurrentPicks[((AppData)this.BindingContext).PickIndex];
}
I have confirmed that the data in the object that should be bound is present and correct, but the PropertyChanged functions for my fields don't get called when the BindingContext is updated.
I am coming back to Xamarin Forms after some time, and I'm sure I'm missing something obvious, but I am missing it nevertheless. Any help is appreciated.
My apologies, this issue appears to be the result of my unfortunate choice of fields to which to bind. There are well over 200 fields in the object, and I happened to select several quite reasonable choices for testing, where the values are unexpectedly null.
Initially, I did have a problem getting the BindingContext set properly, and the field issue prevented me from seeing that I had actually fixed that problem. The binding does in fact seem to be working now that the correct fields are bound.
i create a dependency property to close a view from view model,
dependencyProperty:
public static class WindowBehaviors
{
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.RegisterAttached("IsOpen"
, typeof(bool),
typeof(WindowBehaviors),
new UIPropertyMetadata(false, IsOpenChanged));
private static void IsOpenChanged(DependencyObject obj,DependencyPropertyChangedEventArgs args)
{
Window window = Window.GetWindow(obj);
if (window != null && ((bool)args.NewValue))
window.Close();
}
public static bool GetIsOpen(Window target)
{
return (bool)target.GetValue(IsOpenProperty);
}
public static void SetIsOpen(Window target, bool value)
{
target.SetValue(IsOpenProperty, value);
}
}
and use it in my xaml like this:
<window
...
Command:WindowBehaviors.IsOpen="True">
it work's fine,but when i want to bind it to a property in viewModel,it dosen't work,and i guess,it dosen't work because i define the resource later in xaml.
in xaml:
<Window.Resources>
<VVM:myVieModel x:Key="myVieModel"/>
</Window.Resources>
and i don't know what should i do,where should i put this:
Command:WindowBehaviors.IsOpen="{binding Isopen}"
public MainWindow()
{
InitializeComponent();
// DO THIS
this.DataContext = Resources["myVieModel"];
}
You need to bind the data context for the scope where your binding is in. Usually this is fairly high up in your XAML, usually the first element in your form or control.
In your case, the data context beeing a static resource the folllowing should work:
<grid DataContext="{StaticResource myVieModel}">
<!-- the code with the binding goß into here -->
</grid>
Actually this is the same as ebattulga suggests, just the XAML way (no code behind).
Thanks for your helps,i fixed it and here is my solution,
i used to use MVVMToolkit but now i'm useing MVVMlight and as you know in MVVMLight,we just define Application Resources Once in App.xaml.so we can bind all the window's properties simply,hope this can help some people who has the same problem!!
app.xaml
<Application.Resources>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True" />
</Application.Resources>
and in the window(view)
DataContext="{Binding DefaultSpecItemVM, Source={StaticResource Locator}}"
and it works perfect.:D