I am currently working with a coverflowview by using this nuget:
https://github.com/AndreiMisiukevich/CardView
Works very well with the binding when I use a regular image or cachedimage (ffimageloading nuget). However now i try to grayscale the image by using a custom control. I successfully run the code to grayscale it (when propertyischanged to IsSelectable true), but for some reason the image is not showing at all, if i remove the grayscale logic, the image shows nicely.
<cards:CoverFlowView PositionShiftValue="40"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HeightRequest="360">
<cards:CoverFlowView.ItemTemplate>
<DataTemplate>
<AbsoluteLayout HeightRequest="360">
<controls:GrayScaleImage Aspect="AspectFill"
AbsoluteLayout.LayoutBounds="0.0, 0.5, 1, 1"
AbsoluteLayout.LayoutFlags="All"
Source="{Binding ProgramDeserialized.Image}"
IsSelectable="{Binding IsSelectable}"/>
</AbsoluteLayout>
</DataTemplate>
</cards:CoverFlowView.ItemTemplate>
</cards:CoverFlowView>
And custom control:
public class GrayScaleImage : CachedImage
{
public static BindableProperty IsSelectableProperty = BindableProperty.Create(nameof(IsSelectable), typeof(bool), typeof(GrayScaleImage), true, propertyChanged: UpdateImage);
public bool IsSelectable
{
get { return (bool)GetValue(IsSelectableProperty); }
set { SetValue(IsSelectableProperty, value); }
}
private static void UpdateImage (BindableObject bindable, object oldColor, object newColor)
{
var view = (GrayScaleImage)bindable;
if (!view.IsSelectable)
{
var transformations = new System.Collections.Generic.List<ITransformation>() {
new GrayscaleTransformation()
};
view.Transformations = transformations;
}
}
}
Not sure what the issue might be. When i did it on a regular stacklayout bindable list, and applied the same logic, it works, so my gutfeeling is that there is some issue with the coverflowview nuget.
How did you set binding for the Image? I created a sample to test the function code, the grayScale image works fine.
Check the screenshot:
https://us.v-cdn.net/5019960/uploads/editor/ab/jqki5zvo7cfw.gif
Here is the code about the model class and the viewModel class, you could refer to it.
public class CustomModel
{
public Xamarin.Forms.ImageSource MyImage { get; set; }
public bool IsSelectable { get; set; }
}
public class CustomViewModel
{
public ObservableCollection<CustomModel> Items { get; set; }
public CustomViewModel()
{
Items = new ObservableCollection<CustomModel>();
//add the data
}
}
Related
Hi I would like to know what the easies way to rotate a Grid would be.
I have 4 pages:
private static Figure[] array;
public App ()
{
Initialize(array); // Fills array with figures with ImageSources
InitializeComponent ();
MainPage = new Page(array,Color.Red);
}
class Figure
{
private ImageSource Source {get; set;}
public Figure(ImageSource source)
{
Source = source;
}
}
class Page
{
private Color Color;
private Grid Grid;
public Page (Figure[] Figures, Color color)
{
Color = color;
// Now this is where I need help...
}
}
I would like to have a Grid always the same size and always filled with the same array but depending on the Color the orientation should change. In fact the whole Grid should just rotate 90degrees depending on the Color. These Grids should have ImageButtons which bind to the Imagesource of the figure (with a Converter). I thought about Creating 4Grids in Xaml and implement everything by hand and just give every page the custom Grid. Another option I came up with was creating one Grid only and using the rotation-method of the Grid (but with this option I have to rotate back every child of the Grid as otherwise the pictures would rotate with the Grid... As I think both solutions are quite inconvenient I was wondering what other options I have. Maybe someone can help me? Thanks a lot...
Example of ImageSources that change based on a setting.
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestBugs.MainPage">
<StackLayout>
<Label Text="Test"/>
<Grid ColumnDefinitions="50,50" RowDefinitions="50,50">
<Image Grid.Row="0" Grid.Column="0" Source="{Binding Source1A}" BackgroundColor="Red"/>
<Image Grid.Row="0" Grid.Column="1" Source="{Binding Source1B}" BackgroundColor="Green"/>
<Image Grid.Row="1" Grid.Column="0" Source="{Binding Source2A}" BackgroundColor="Blue"/>
<Image Grid.Row="1" Grid.Column="1" Source="{Binding Source2B}" BackgroundColor="Yellow"/>
</Grid>
</StackLayout>
</ContentPage>
C#:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TestBugs
{
public partial class MainPage : ContentPage
{
// REPLACE "TestBugs" with your project's assembly name.
public const string AssemblyName = "TestBugs";
public enum Orientation
{
One, Two, Three, Four
}
const int NOrientations = 4;
public MainPage()
{
// Assuming stored locally in files or resources.
// If need server queries, recommend not doing this in constructor.
LoadOurImages();
InitializeComponent();
// In this simple example, the binding sources are in the page itself.
BindingContext = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
BackgroundTestLoop();
}
static Random Rand = new Random();
private void BackgroundTestLoop()
{
Task.Run(async () =>
{
const int NTimes = 20;
for (int i = 0; i < NTimes; i++)
{
await Task.Delay(3000);
Orientation nextOrient = (Orientation)Rand.Next(NOrientations);
// Only affect UI from main thread.
Device.BeginInvokeOnMainThread(() =>
{
Orient = nextOrient;
});
}
});
}
public Orientation Orient {
get => _orient;
set
{
_orient = value;
// When Orient changes, that affects the values of these properties.
// OnPropertyChanged is from super-class BindableObject.
OnPropertyChanged(nameof(Source1A));
OnPropertyChanged(nameof(Source1B));
OnPropertyChanged(nameof(Source2A));
OnPropertyChanged(nameof(Source2B));
}
}
private Orientation _orient = Orientation.One;
// Public getters. These change when Orient changes.
public ImageSource Source1A => Sources[Indexes1A[(int)Orient]];
public ImageSource Source1B => Sources[Indexes1B[(int)Orient]];
public ImageSource Source2A => Sources[Indexes2A[(int)Orient]];
public ImageSource Source2B => Sources[Indexes2B[(int)Orient]];
List<string> ResourcePaths = new List<string> {
"apple.png", "banana.png", "car.png", "dog.png"};
List<ImageSource> Sources = new List<ImageSource>();
// Change these as needed.
List<int> Indexes1A = new List<int> { 0, 1, 2, 3 };
List<int> Indexes1B = new List<int> { 1, 2, 3, 0 };
List<int> Indexes2A = new List<int> { 2, 3, 0, 1 };
List<int> Indexes2B = new List<int> { 3, 0, 1, 2 };
private void LoadOurImages()
{
foreach (var path in ResourcePaths)
Sources.Add(CreateOurSource(path));
}
private ImageSource CreateOurSource(string resourcePath)
{
// For embedded resources stored in project folder "Media".
var resourceID = $"{AssemblyName}.Media.{resourcePath}";
// Our media is in the cross-platform assembly. Find that from this page.
Assembly assembly = this.GetType().GetTypeInfo().Assembly;
ImageSource source = ImageSource.FromResource(resourceID, assembly);
return source;
}
}
}
I am using OxyPlot to show a basic line-Chart under WPF/.net 4.5 and the relevant part of the XAML looks like this:
<oxy:Plot Title="{Binding Title}">
<oxy:Plot.Series>
<oxy:LineSeries ItemsSource="{Binding Points}"/>
<oxy:LineSeries ItemsSource="{Binding Points2}"/>
</oxy:Plot.Series>
<oxy:Plot.Axes>
<oxy:CategoryAxis Position="Bottom" ItemsSource="{Binding Labels}" />
</oxy:Plot.Axes>
</oxy:Plot>
The code to create the chart is this:
public class MainViewModel
{
public MainViewModel()
{
this.Title = reader.title;
this.Points = reader.mylist;
this.Points2 = reader.mylist2;
this.Labels = reader.labs;
}
public string Title { get; private set; }
public IList<DataPoint> Points { get; private set; }
public IList<DataPoint> Points2 { get; private set; }
public IList<String> Labels { get; private set; }
}
The properties are set and changed on demand in another method basically simply like this:
public static List<DataPoint> mylist = new List<DataPoint>();
public static List<DataPoint> mylist2 = new List<DataPoint>();
public static List<String> labs = new List<String>();
public static string title;
public void setchart()
{
mylist.Add(new DataPoint(0,5));
mylist.Add(new DataPoint(1,10));
mylist2.Add(new DataPoint(0,30));
mylist2.Add(new DataPoint(1,25));
labs.Add("date1");
labs.Add("date2");
title = "mytitle";
MainWin.myPlot.InvalidatePlot(true);
}
It works for the lines in the chart (Points, Points2) and the labels on the x-axis (Labels). But the title of the chart just doesn't update - "mytitle" never appears as title of the chart unless I put it like
this.Title = "mytitle";
directly into MainViewModel().
Some people seem to have similar problems (e.g. here) but I would like to know if I'm doing something wrong.
On your MainViewModel contructor, when you assign the reader Lists to the ILists, both will point to the same object, so when you modify one, the other will be modified too.
String, on the other hand, although is a reference type, it is imutable, so it behaves pretty much like a value type. So when you modify the string in the reader class, you're not modifying the one in the ViewModel.
I am having a little Problem with DataBinding to a ListView.
Because I want to have a Listview with MultiSelection I needed to implement a custom class called GenericSelectableItem which stores the Data, and if the cell IsSelected.
First, here is the View Model of the MainPage:
public class MainPageViewModel : BaseViewModel, INotifyPropertyChanged
{
private ObservableCollection<GenericSelectableItem<AudioFile>> _audiofiles = new ObservableCollection<GenericSelectableItem<AudioFile>>();
public ObservableCollection<GenericSelectableItem<AudioFile>> AudioFiles
{
get => _audiofiles ?? new ObservableCollection<GenericSelectableItem<AudioFile>>();
set
{
_audiofiles = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(AudioFiles)));
}
}
}
The Xaml for the MainPage:
<!-- The Content -->
<ListView x:Name="listView" Grid.Row="1" HasUnevenRows="true" RowHeight="-1" ItemsSource="{Binding AudioFiles}" ItemSelected="ListView_OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<local:AudioViewCell Audiofile="{Binding Data}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
2 Helper Classes for making a multiselectable ListView:
public class GenericSelectableItem<T> : SelectableItem
{
public GenericSelectableItem(T data)
: base(data)
{
}
public GenericSelectableItem(T data, bool isSelected)
: base(data, isSelected)
{
}
// this is safe as we are just returning the base value
public new T Data
{
get => (T)base.Data;
set => base.Data = value;
}
}
public class SelectableItem : BindableObject
{
public static readonly BindableProperty DataProperty =
BindableProperty.Create(
nameof(Data),
typeof(object),
typeof(SelectableItem),
(object) null);
public static readonly BindableProperty IsSelectedProperty =
BindableProperty.Create(
nameof(IsSelected),
typeof(object),
typeof(SelectableItem),
(object)false);
public SelectableItem(object data)
{
Data = data;
IsSelected = false;
}
public SelectableItem(object data, bool isSelected)
{
Data = data;
IsSelected = isSelected;
}
public object Data
{
get => (object)GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
public bool IsSelected
{
get => (bool)GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
}
A Binding Example in AudioViewCell.Xaml:
<Label x:Name="LblFilename" Text="{Binding Filename}"
VerticalTextAlignment="Center"
Style="{StaticResource CellLabel}"/>
The AudioViewCell.cs
public partial class AudioViewCell : ViewCell
{
public static BindableProperty AudiofileProperty = BindableProperty.Create(
propertyName: nameof(Audiofile),
returnType: typeof(AudioFile),
declaringType: typeof(AudioViewCell),
defaultValue: null,
defaultBindingMode: BindingMode.OneWay);
public AudioFile Audiofile
{
get => (AudioFile) GetValue(AudiofileProperty);
set
{
Debug.WriteLine("Audiofile changed");
SetValue(AudiofileProperty, value);
((MenuItemViewModel) BindingContext).Audiofile = value;
}
}
public AudioViewCell()
{
InitializeComponent();
this.BindingContext = new MenuItemViewModel(SlAdditionalData, AwvWaveView);
}
}
And finally the MenuItemViewModel:
public class MenuItemViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private AudioFile _audioFile;
public AudioFile Audiofile
{
get => _audioFile;
set
{
Debug.WriteLine("Setting Audiofile");
_audioFile = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Audiofile)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Filename)));
}
}
public string Filename => Audiofile?.Filename;
}
It seems that the Field Data inside GenericSelectableItem is never set so I think there is something wrong with the binding
Does anyone know a better way or why this is not working?
Thanks alot for your help!!
TL;DR Version: Taking a deep looking on your cell's and 'cellViewModel's source code I've noticed that there's a confusion on bindings handle on your code. You are treating one BindingContext that you set at the AudioViewCell's constructor but it's overridden by the one set automatically by the ListView (that runs after the constructor). So you stand with a ViewCell rendered with no data.
On this image, I tried to show what's going on with your model:
Notice that yellow circular arrow at left, it's you defining the binding context at the constructor. That's overridden later by the red arrows (set after the listview renders).
To make it works the way you've coded, follow these steps:
Get rid of the AudiofileProperty at AudiofileViewCell, you will not need it;
Create an overload to MenuItemViewModel's constructor to receive the "AudioFile" (the MenuItemViewModel class is your real BindingContext);
Override the OnBindingContextChanged method to extract the new one Data field and send it as a parameter to the constructor of a new instance of MenuItemViewModel;
Set this new Instance of MenuItemViewModel as BindingContext of your inner View (It's a StackLayout called slRoot according to your source code)
Here's the steps code:
2:
public class MenuItemViewModel : INotifyPropertyChanged
{
// ...
public void SetAudiofile(AudioFile data)
{
Audiofile = data;
}
// ...
}
3 and 4:
public partial class AudioViewCell : ViewCell
{
// ...
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
// * I'm not sure if it's ok create a new instance to your binding context here, the old one ca be kept on memory due it's subscription. Think about create a method to set just the Audiofile property
slRoot.BindingContext = new MenuItemViewModel( thing, thing, ((GenericSelectableItem<AudioFile>)BindingContext).Data);
}
// ...
}
I've tested and it works, but it's far from an ideal clean solution.
If your intent is to reuse this cell, I think you should expose the properties that can be or not bound, let the need of it says what will be shown. The view cell should only handle visual layout / behavior, don't matter what data is on it.
P.S.: Sorry for my bad English, I hope it can be understandable.
I'm creating a WPF program and I have created a custom Usercontrol and custom Textbox
When I rebuild my solution in visual studio i get this error.
Cannot set Name attribute value 'SearchT' on element 'HintTextBox'. 'HintTextBox' is under the scope of element 'ClickableControl', which already had a name registered when it was defined in another scope
I don't know what I need to do. Or what I did wrong? can someone help me? The classes below are the usercontrol and the hinttextbox, the last one is how I implmented them in xaml.
This is how I put the textbox in my Usercontrol
TEXTBOX = HintTextBox:
namespace View.custom_usercontrols
{
public partial class HintTextBox : TextBox
{
public static readonly DependencyProperty HintepDependencyProperty = DependencyProperty.Register("Hint", typeof(string), typeof(HintTextBox));
public string Hint
{
get
{
return (string)GetValue(HintepDependencyProperty);
}
set
{
SetValue(HintepDependencyProperty, value);
}
}
private string _text;
private bool _placeHolder;
public HintTextBox()
{
InitializeComponent();
if (Hint == null)
{
_text = "";
}
else
{
_text = Hint;
}
_placeHolder = true;
Text = _text;
Opacity = 0.2;
}
//extra code
}
}
This is my UserControl = ClickableControl
namespace View.custom_usercontrols
{
[ContentProperty(nameof(Children))]
public partial class ClickableControl : UserControl
{
public static readonly DependencyPropertyKey ChildrenProperty = DependencyProperty.RegisterReadOnly(
nameof(Children), // Prior to C# 6.0, replace nameof(Children) with "Children"
typeof(UIElementCollection),
typeof(ClickableControl),
new PropertyMetadata());
public static readonly DependencyProperty HoverColorDependencyProperty = DependencyProperty.Register("HoverColor", typeof(Brush), typeof(HintTextBox));
public static readonly DependencyProperty SelectedColorDependencyProperty = DependencyProperty.Register("SelectedColor", typeof(Brush), typeof(HintTextBox));
public static readonly DependencyProperty SelectedDependencyProperty = DependencyProperty.Register("Selected", typeof(Boolean), typeof(HintTextBox));
public Brush HoverColor
{
get
{
return (Brush)GetValue(HoverColorDependencyProperty);
}
set
{
SetValue(HoverColorDependencyProperty, value);
}
}
public Brush SelectedColor
{
get
{
return (Brush)GetValue(SelectedColorDependencyProperty);
}
set
{
SetValue(SelectedColorDependencyProperty, value);
}
}
private Brush BackgroundColor { get; set; }
public Boolean Selected
{
get
{
return (Boolean)GetValue(SelectedDependencyProperty);
}
set
{
SetValue(SelectedDependencyProperty, value);
if (value)
{
Background = SelectedColor;
}
else
{
Background = BackgroundColor;
}
}
}
public UIElementCollection Children
{
get { return (UIElementCollection) GetValue(ChildrenProperty.DependencyProperty); }
private set { SetValue(ChildrenProperty, value); }
}
public ClickableControl()
{
InitializeComponent();
Children = Grid.Children;
}
//EXTRA CODE
}
}
XAML:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:View"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:customUsercontrols="clr-namespace:View.custom_usercontrols"
//somewhere in the layout
<customUsercontrols:ClickableControl MouseDown="Search_OnMouseDown"
GotFocus="Search_OnGotFocus"
Background="#444444">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Magnify"
Margin="25 0 0 0"
Height="25"
Width="25"
Foreground="White"
VerticalAlignment="Center"/>
<customUsercontrols:HintTextBox x:Name="SearchT"
Padding="15"
Hint="SEARCH"
Width="204">
</customUsercontrols:HintTextBox>
</StackPanel>
</customUsercontrols:ClickableControl>
Thank you verry mutch
This is a bit late, but for anyone that views this question and still wonder about it, here goes:
Don't inherit from UserControl(Which inherits from contentControl) and then change default Content property of it, and expect it's content to be recognized upon call to InitializeComponent();
The elements "inside" the UserControl are its Content. if you defer its content to another property, stuff will go haywire.
Either you put the control you want to name under the UserControl xaml definition(the usual way), or you add it in code behind and name it,
or you can create a custom control and set its ControlTemplate with the control you want and specify it as a PART of the control:
http://paulstovell.com/blog/wpf-part-names
I am using WPF (C#) for the first time and this I've encountered my first "real" design choice. I have a main window and when the user enters some data and presses the "plot" Button, a new window will come up showing a graph.
This graph window I am defining myself with a combination of xaml and the code-behind file. The issue is that 2 parameters this window has is the x axis title and the y axis title. So, these should be "parameters" to making this window.
I am confused by this because I'm using MVVM and I have a "ViewModel" for the window called GraphWindowPresenter and a "View" for the class called GraphWindowView.
At first, I tried to have an xAxis property and a yAxis property in my GraphWindowPresenter but that will not work since I need to "bind" to these values upon construction of the GraphWindowView. Additionally, this approach would require that my GraphWindowPresenter take an xAxis parameter and a yAxis parameter which is problamatic as well since I just create an instance of the class in the xaml of GraphWindowView.
I'm thinking of a possible soltuion that I can just have my GraphWindowView take the xAxis and yAxis parameters but doesn't this violate MVVM? I would rather not do that.
Note: This is similar to this post MVVM: Binding a ViewModel which takes constructor args to a UserControl. But in my scenario it is tricky since I have a parent window and a pop up child window.
Question: What is the best approach to this design issue? What are the "best practices" regarding this scenario?
Possible Answer:
Is this the correct use of dependency properties that you described? Is this a "clean" solution?
private void doGraph()
{
if (log == null) // if a log is not loaded
{
MessageBoxResult mbr = MessageBox.Show("A log file must be " +
"loaded before plotting.",
"Warning",
MessageBoxButton.OK,
MessageBoxImage.Exclamation);
return;
}
// NOW MUST PRESENT GRAPH WINDOW
GraphWindowView gwv = new GraphWindowView();
gwv.xAxis = X_AXIS_VALUE:
gwv.yAxis = Y_AXIS_VALUE;
gwv.Show();
}
And in my GraphWindowView class I have the code:
public partial class GraphWindowView : Window
{
// Using a DependencyProperty as the backing store for yAxis.
public static readonly DependencyProperty yAxisProperty =
DependencyProperty.Register("yAxis", typeof(string), typeof(GraphWindowView));
// Using a DependencyProperty as the backing store for xAxis.
public static readonly DependencyProperty xAxisProperty =
DependencyProperty.Register("xAxis", typeof(string), typeof(GraphWindowView));
public string xAxis
{
get { return (string)GetValue(xAxisProperty); }
set { SetValue(xAxisProperty, value); }
}
public string yAxis
{
get { return (string)GetValue(yAxisProperty); }
set { SetValue(yAxisProperty, value); }
}
public GraphWindowView()
{
InitializeComponent();
}
}
You can you userSetting properties
One my application have same scenario in that i have mainWindow that accept HostAddress,Port value and it will use another window when i click connect so i am using userSetting properties. I am also using MVVM pattern check code snippet below
XAML:
<TextBox Width="120" Canvas.Left="132" Canvas.Top="16" Text="{Binding Path=Server,Mode=TwoWay}"/>
<TextBox Width="120" Canvas.Left="132" Canvas.Top="42" Text="{Binding Path=DisplayPort,Mode=TwoWay}"/>
<TextBox Width="120" Canvas.Left="132" Canvas.Top="69" Text="{Binding Path=CtrlPort,Mode=TwoWay}"/>
<Button Content="Launch" Name="btnLaunch" Command="{Binding Path=appSetting}" Canvas.Left="132" Canvas.Top="100" Width="120" Height="51" Click="btnLaunch_Click" />
VIEWMODE:
public class SettingsViewModel : ViewModelBase
{
private Settings _settings { get; set; }
public SettingsViewModel()
{
appSetting = new RelayCommand(this.AppSettingsCommand);
_settings = ApplicationTest.Properties.Settings.Default;
}
private string _server = Settings.Default.Server;
public string Server
{
get { return this._server; }
set
{
if (this._server != value)
{
this._server = value;
OnPropertyChanged("Server");
}
}
}
private string _displayPort = Settings.Default.DisplayPort;
public string DisplayPort
{
get { return this._displayPort; }
set
{
if (this._displayPort != value)
{
this._displayPort = value;
OnPropertyChanged("DisplayPort");
}
}
}
private string _ctrlPort = Settings.Default.CtrlPort;
public string CtrlPort
{
get { return this._ctrlPort; }
set
{
if (this._ctrlPort != value)
{
this._ctrlPort = value;
OnPropertyChanged("DisplayPort");
}
}
}
public RelayCommand appSetting
{
get;
set;
}
private void AppSettingsCommand()
{
this._settings.Server = this.Server;
this._settings.DisplayPort = this.DisplayPort;
this._settings.CtrlPort = this.CtrlPort;
this._settings.Save();
}