C#, xamarin, binding many objects - c#

So I've got a bunch of Frame objects that I'm adding to the display via code (the count of them is variable). And being true to MVVM, I need to bind the background color of each to something in the model. So the bind is simple:
Frame f1 = new Frame();
f1.SetBinding(Frame.BackgroundColorProperty, "BackColor1");
Frame f2 = new Frame();
f2.SetBinding(Frame.BackgroundColorProperty, "BackColor2");
And so on. That's just a quick example, the real thing is in a loop. And in the model:
Xamarin.Forms.Color BackColor1
{
get
{
return Color.Black;
}
}
Xamarin.Forms.Color BackColor2
{
get
{
return Color.White;
}
}
And so on. This works fine for a few objects, but what if there's 50? 100? I don't want to create a 100 BackColor properties in the class; that way lies madness. I've only being playing around in C# and xamarin for a few weeks now, so there's much I've still got to learn. What would be the best way to stay true to MVVM when you need to bind a large number of objects like this?

Are those colors gong to change once assigned? If so no need to stay in "true MVVM," which I assume you mean "using bindings." True MVVM does not require using bindings for everything, in fact if a property's value won't change once the object is instantiated, then using a binding is overkill. It is OK to use static values for properties and still be within "true MVVM." In fact static values for properties are better if the values are not going to be changed, or even not changed often, since bindings do have some performance overhead. So basically, every time you instantiate a new Frame, just set the BackgroundColor statically.

Related

I'm trying to design a solution for a day/night color scheme and got stuck

I'm working on an app that has a "day" and "night" color palette that can change automatically. We're using Xamarin Forms and, for historical reasons, we're not using XAML but I speak XAML so I'm going to use it in this post.
I've approached it by creating a base type with a property for relevant colors like "dark text" or "header background", then implementing that type for both schemes. Then, I made a type that references one of those and raises a PropertyChanged even if it changes. So a day->night transition involves setting the property, then anything in the UI bound to a path like "ColorScheme.DarkText" changes. Nice.
Now I've got a ViewModel that wants to have different colors for some items in a list. I want those colors backed by this day/night change system, but I might have designed myself into a corner. I'll show you what I did and how I want to redesign, but I'm curious if there's a clever way to go about it without causing other problems.
Here's a VM for an item I'm binding to, let's all assume there's nothing unexpected in ViewModelBase:
public class ItemViewModel : ViewModelBase {
public string IconColorName { get...; set...; }
public string IconText { get...; set...; }
}
That ViewModel's contained in another boring ViewModel that makes up the rest of the page:
public class PageViewModel : ViewModelBase {
public ObservableColorScheme ColorScheme { get...; set...; }
public ObservableCollection<ItemViewModel> Items { get...; set...; }
}
OK, so what I'm going for is I'd like XAML for my item's template to look something like:
<StackLayout>
<StackLayout.Children>
<Label TextColor={Binding IconColor, Converter={StaticResource StringToColorConverter} />
...
</StackLayout.Children>
</StackLayout>
Right. OK. So now here's the problem. I can imagine building that IValueConverter and setting it up so it has the same concept of the right color scheme, then using the string value here to get the appropriate property. But I have a problem: there can only be one source for a binding, right? I need the color to change if EITHER the ColorScheme or IconColorName changes. My hunch is WPF could do that, but Xamarin can't?
The most obvious solution I've thought of is some kind of extra ViewModel, in XAML-unfriendly format for brevity:
public class ColorViewModel : ViewModelBase {
public Color Color { get...; set...; }
public ColorViewModel(string colorName, ObservableColorScheme colorScheme) {
colorScheme.PropertyChanged += (s, e) => {
if (e.PropertyName == colorName) {
Color = colorScheme.Get(colorName);
}
}
Color = colorScheme.Get(colorName);
}
}
I do NOT like this. These items are created and destroyed a lot, so that means that event handler needs to be unsubscribed. I don't want to have to think about that, and I can assume a maintenance programmer will forget. I've thought about retooling it to use a WeakReference for the event subscription but... that's getting really icky.
So I'm not really sure how to proceed, short of making the Page here detect color scheme changes and manually update its child views. That feels icky too. I've been thinking about it for a couple of days and nothing nice is presenting itself.
I'm open to "you're doing this terribly wrong, and there's some feature that would make this dramatically easier for you". I'm suspicious that feature is Styles, which I'm not using because 1) the aforementioned lack of using XAML and 2) our project is older than Style support in Xamarin Forms. Feel free to tell me to throw this design away, but please don't do so without showing me a quick example of the better way!
I don’t know xamarin nor C#, so maybe I’ll use the wrong terms, but this is how I would approach this problem:
I assume you have some kind of ViewModel hierarchy and you know the root of this hierarchy.
I would create a ColorScheme class with a static getter of the current color scheme and getters for each color. So you can create a subclass for day and night scheme. Also a void ApplyTo(ViewModelBase). I would create an ViewModelBase interface which has an void UpdateColorScheme() and List Items().
Ok, now, each ViewModel can use the UpdateColorScheme method to setup the view will be created.
Probably there is some kind of event to change the color scheme. This could be a button or a clock based trigger. This event sets the correct ColorScheme and simply calls ColorScheme.CurrentScheme().ApplyTo(rootViewModel). The ApplyTo method walks down the ViewModel hierarchy and calls UpdateColorScheme() for each ViewModel.
This isn’t very fancy but your don’t have to create dozens of objects for something which will change rarely (only twice a day). You only have one instance of ColorScheme and be instance for each Color and a separate method for color settings. But you don’t have to create and register events listeners all the time. The base class will enforce the maintenance programmer to use this concept so he can not forget to setup a event listener and you use the same code for setting up and updating the view.
And a little side note: opinion based questions are not allowed on Stack Overflow. There is a Core Review Page of StackOverflow, I think this question belongs there.

LayoutParams change only takes effect in fullscreen

im using Xamarin with MvvmCross.
Ive done a FragmentDialog with a recyclerView inside, the list is populated via bindings on xml file, so i have no adapter and i should keep it this way.
If im not wrong, theres no built in way to make the recyclerView take only the size needed for its content, this should not be a problem, but in this case i need the list to start from bottom...
So i did this (its a custom fullscreen dialog) :
MvxRecyclerView list = Dialog.FindViewById<MvxRecyclerView>(Resource.Id.recyclerview);
list.LayoutChange += List_LayoutChange;
Then in layoutChange
private void List_LayoutChange(object sender, View.LayoutChangeEventArgs e)
{
MvxRecyclerView list = Dialog.FindViewById<MvxRecyclerView>(Resource.Id.recyclerview);
int itemHeight = list.GetChildAt(0).Height;
if (itemHeight != 0)
{
ViewGroup.LayoutParams prms = list.LayoutParameters;
prms.Height = itemHeight * list.GetAdapter().ItemCount;
list.LayoutParameters = prms;
list.LayoutChange -= List_LayoutChange;
list.RequestLayout();
}
}
That was working fine, the list get exactly the height needed and the list looks like it starts from bottom.
Now the client tell me that he doesnt like the fullscreen dialog and wants the status bar, i think that should be easy, just to remove this line at the dialog creation right?
dialog.Window.AddFlags(WindowManagerFlags.Fullscreen);
But looks like its not that easy, when the dialog its not fullscreen the layoutParams change seems to have no effect, it just dont do nothing.
My method is being called and i get the right item height, it just dont change the recyclerview height.
Notice that setting fullscreen at creation and clearing the flag after the recyclerview params change works
So looks like it only works during fullscreen mode.
Can someone throw some light at this?
Thanks in advance.
As you said, RecyclerView was not aware of its size.
Since last update to the support lib, it is !
http://android-developers.blogspot.fr/2016/02/android-support-library-232.html
The RecyclerView widget provides an advanced and flexible base for creating lists and grids as well as supporting animations. This release brings an exciting new feature to the LayoutManager API: auto-measurement! This allows a RecyclerView to size itself based on the size of its contents. This means that previously unavailable scenarios, such as using WRAP_CONTENT for a dimension of the RecyclerView, are now possible. You’ll find all built in LayoutManagers now support auto-measurement.
I would suggest to wait for the Xamarin wrapped lib (there is already a beta https://www.nuget.org/packages/Xamarin.Android.Support.v4/23.2.0-beta1)

how could i do this in a smarter way ? (list of images to set a status on)

I'm still in the beginning stage of learning c#, so i have a question about a part of my code.
I strongly have the feeling there should be a way smarter way to accomplish what i try to do:
So what do i have:
A WPF window, with a whole bunch of images on it.
At the start, i want them all to be set to Hidden, so at the initialize part of the window, i set them all hidden and do it like this:
.....
IMx01y01W.Visibility = Visibility.Hidden;
IMx23y73W.Visibility = Visibility.Hidden;
IMx31y21W.Visibility = Visibility.Hidden;
IMx03y16W.Visibility = Visibility.Hidden;
.....
There is a logical in the names of the images like IMx##y##W, where the ## are the variable numbers.
As stated above, i strongly have the feeling that there should really be some smarter way to do this.
=======
EDIT 1
Ok so far i ended up with this:
foreach (object obj in LogicalTreeHelper.GetChildren(this))
{
if (obj is Image)
{
Image obj = (Image)item;
obj.Visibility = Visibility.Hidden;
}
//do something
}
The part in the if statement is totally wrong, but i don't know how to go on on this point. can anyone push me a bit more in the right direction ?
thanx!
You can frame this requirement as "set the Visibility property of the various images to the same value", i.e. they all show/hide based on the same exact flag. WPF has some mechanisms that require a slightly different mindset than traditional imperative code, but can greatly simplify this scenario.
WPF supports the concept of databinding, which allows you to declare the images' dependency on a value instead of applying the value by hand. You can take advantage of this to manage the Visibility property values solely in markup.
The main thing to learn about is the DataContext property, which represents the object currently bound to a particular control. In this case, you can set the data context to a value of Visibility:
public MainWindow()
{
DataContext = Visibility.Hidden;
}
Once you do that, it cascades throughout the object tree, so all of the children of MainWindow inherit this same value. This means you can do this in your XAML:
<Image x:Name="IMx01y01W" Visibility="{Binding}" />
<Image x:Name="IMx23y73W" Visibility="{Binding}" />
That tells WPF that you want the Visibility property to reflect the value of the current DataContext, which will be the Visibility.Hidden value you set in the constructor.
The nice part about this is that if you change the value of the DataContext property, it will automatically be reflected in all your images without any extra work:
private void MakeImagesVisible()
{
DataContext = Visibility.Visible;
// At this point, all images will be visible
}
The key takeaway is that WPF does things in a different way than traditional UI paradigms such as Windows Forms. Learning the philosophy behind these new techniques, rather than replicating legacy techniques, will help make WPF easier to understand and bend to your will.
Yes there is. You can get all children of a container control and filter for a particular type, images in your case, and perform actions on those instances. See this forum post for a sample. And this SO post. And this one.
For EDIT 1:
foreach (object obj in LogicalTreeHelper.GetChildren(this))
{
if (obj is Image)
{
Image img = (Image)obj;
img.Visibility = Visibility.Hidden;
}
}
One thing that you can do is
IMx01y01W.Visibility = IMx23y73W.Visibility = ... = Visibility.Hidden;
but I wouldn't call this any more readable.
What you could do instead is create a collection of IMx01y01W objects (of their base class) and iterate through that collection settings Visibility to Hidden.

How to change background of UITableViewController in GroupedStyle

I'm trying to change the color of a UITableViewController when using a split view. Then the style is set to grouped, the background seems to light gray no matter what.
I create a new iPad Split Application solution and add the following code to RootViewController:
public RootViewController () : base( UITableViewStyle.Grouped) // base ("RootViewController", null)
{
this.TableView.BackgroundColor = UIColor.Red;
this.View.BackgroundColor = UIColor.Red;
this.View.Layer.BorderColor = UIColor.Green.CGColor;
this.TableView.BackgroundView.BackgroundColor = UIColor.Brown;
I'm pulling my hair out trying to work out why its always light gray.
You do not change the color of the controller but of one (or many) of its views.
Also it's generally TintColor that must be used (not BackgroundColor). A bit confusing (I think it's related to the use of gradients, over the background, used in many place - but I could be wrong).

How can I write a BringToFront method for a WPF UserControl?

I am developing a UserControl, call it CoolControl, that is meant to act somewhat like a window, with a few special features. So far, it can be resized and dragged all around the screen. If I add multiple CoolControl objects to my application window using XAML, the last one that was declared is always in front. This is fine, but I want to make it so that if I click on one of my CoolControl objects during run-time, that control will put itself in front of all the other controls.
I've tried using Canvas.SetZIndex, but unless I'm simply unable to come up with a clever enough solution, I don't see how that can help me. Because once I set one control's Z-Index to 9999, over time every other control I click will have the same value of 9999. And then, once again, the control declared last ends up in front.
If you were given the task of writing a BringToFront() method for someone's UserControl, how would you do it in the simplest way possible? I'd prefer a better solution than getting the parent window, looping through all the controls, finding the maximum Z-Index, and then setting the Z-Index of the CoolControl accordingly, if THAT is even a valid solution.
I'm not familiar with the Canvas.SetZIndex method. It looks like some sort of attached property or behaviour.
If you can provide the logic to set the z-index, I've outlined a way to keep track of the instances and manage the z-indexes, keeping them in the order in which they have been selected/created.
public class CoolControl : UserControl
{
public CoolControl()
{
InitializeComponent();
Instances.Add(this);
}
static IList<CoolControl> Instances = new List<CoolControl>();
void SelectThisInstance()
{
foreach(var instance in Instances)
{
// decrement z-index instance
}
// set z-index of this instance to show at top
}
}

Categories