I'm writing a maui desktop application view that genereates blocks based on items in a collection. So I have an observable Collection called Pedals which I want ListView to enumerate through and generate a ViewCell based on defined templates I have that all works fine. Inside my ViewCell however the Databinding cannot seem to find the item they are being used to represent in.
The example Im working on there should be two objects in Pedals a ReverbPedal object and Distortion Pedal object (Both Observable Objects with Observable Properties) and my template should load one template for reverb and another for distortion.
In the block for the reverb pedal distortion I Cannot Seem to access the properties of the Classes. Each Pedal object inherits from an Interface with the parameters property. Here is a snippet of my xaml code currently its a mess from debugging but you can see all the different things I've tried
<ListView x:Name="listView" ItemsSource="{Binding Pedals}" SelectedItem="{Binding SelectedPedal, Mode=TwoWay}" >
<ListView.ItemTemplate>
<templates:PedalDataTemplateSelector>
<templates:PedalDataTemplateSelector.SchroederReverbPedalTemplate BindingContext="{Binding .}">
<DataTemplate>
<ViewCell x:Name="parameterControls">
<StackLayout Orientation="Vertical">
<Slider x:Name="delaySlider" Value="{Binding Path=ObservableParameters[Delay]}" Minimum="0" Maximum="1500"/>
<Label x:Name="delaySlidervalueLabel" Text="{Binding Value, Source={x:Reference delaySlider}, StringFormat='{0:F1}'}"/>
<Label Text="{Binding .}"/>
<Label Text="{Binding Path=item.ObservableParameters[Delay], Source={RelativeSource AncestorType={x:Type DataTemplate}}}"/>
<Label Text="Delay"/>
<Label Text="Decay"/>
<Slider x:Name="decaySlider" Value="{Binding ObservableParameters[Decay], Source={x:Reference listView}}" Minimum="0" Maximum="1000"/>
<Label x:Name="decaySlidervalueLabel" Text="{Binding Value, Source={x:Reference decaySlider}, StringFormat='{0:F1}'}"/>
<Label Text="Mix %"/>
<Slider x:Name="mixSlider" Value="{Binding ObservableParameters[MixPercent], Source={x:Reference listView}}" Minimum="0" Maximum="100"/>
<Label x:Name="mixSlidervalueLabel" Text="{Binding Value, Source={x:Reference mixSlider}, StringFormat='{0:F1}'}"/>
<Switch IsToggled="{Binding IsEnabled, Source={x:Reference listView}}" OnColor="Pink" ThumbColor="Purple"/>
<Button Text="Print Debug" Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:PedalBoardViewModel}}, Path=PrintDebugCommand}" />
</StackLayout>
I'm not sure if this helps but here is the code for my datatemplateselecetor incase something is wrong there
public class PedalDataTemplateSelector : DataTemplateSelector
{
public DataTemplate SchroederReverbPedalTemplate { get; set; }
public DataTemplate OverDrivePedalTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var viewCell = new ViewCell();
if (item is SchroederReverb)
{
return SchroederReverbPedalTemplate;
}
else if (item is OverDriveDistortion)
{
viewCell.BindingContext = item;
return OverDrivePedalTemplate;
}
return null;
}
}
I have tried everything and I thought I had a solution using SelectedPedal but that breaks down when more than one pedal is used because it starts editing all of their values in sync. I'm not sure what else to try I've tried setting the listview as the relativesource and no matter what it cannot seem to find the object.
My only lead on what to do involves that empty label in there to test source of the Bind and doing {Binding .} gets the correct object but I cannot get it to use that same data context for the sliders. (Doing Just {Binding Parameters} Throws error that it cannot find parameters binding on the viewmodel. I've also made sure that everything I'm trying to bind to is an observable property.
Finally Heres my model code, Pedals is collection of the IPedal type and I had to write an observable wrapper for my base class of reverb pedal but this wrapper includes all the info that is relevant the base class just has methods to run reverb algorithms. The base contains a parameters dictionary and implements IPedals and I am just using this class to wrap those as observable
public partial class ObservableSchroederReverb : SchroederReverb
{
public Dictionary<string, float> ObservableParameters
{
get => Parameters;
set
{
Debug.WriteLine($"Chaning to {value}");
SetProperty(Parameters, value, this, (u, n) => u.Parameters = n);
}
}
public ObservableSchroederReverb(IAudioData audioFile, float delay, float decay, float mixPercent) : base(audioFile, delay, decay, mixPercent)
{
{
Parameters = new Dictionary<string, float>()
{
{ "Delay", delay },
{ "Decay", decay },
{ "MixPercent", mixPercent }
};
Delay = delay;
Decay = decay;
MixPercent = mixPercent;
AudioFile = audioFile;
IsEnabled = false;
}
}
}
CodePudding user response:
this line
x:DataType="viewmodel:PedalBoardViewModel"
tells VS that the VM for the page is of type PedalBoardViewModel
. It is purely a helper method for Intellisense and not required. However, if you have it it thinks that ALL binding expressions on the page are properties of that VM. This is a problem when you have templated views like ListView
, because the template will have a different BindingContext
(IPedal
) than the rest of the page.
You can solve that by adding another DataType
to just your DataTemplate
like this
x:DataType="viewmodel:IPedal"
however, in your case this won't help because you want to use properties that don't exist on IPedal
So the answer for this specific case is to get rid of DataType
completely
then you can use this in your binding expression
Value="{Binding ObservableParameters[Decay]}"
you shouldn't need to specify a source