How to change background of UITableViewController in GroupedStyle - c#

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).

Related

How to replace MapView with separate Pins?

I'm trying to change MapView in my Xamarin Forms app (with Mapsui and Prism), as I need separate view to store Pins. First MapView (let's call it default) is for displaying all pins from list. Second MapView (history) is for displaying new pins, which are removed when leaving Page.
I have Dictionary for storing my MapViews to have easy way to access any view I want. Every created MapView has the same instance of Map. I want to change from default to history which should hide pins that are added in default view.
I change view with this:
public void ChangeView(string name)
{
var prev = MapView;
loggerService.Info($"Pins: {prev.Pins.Count}");
MapView = GetView(name); // Current MapView in XAML, returns instance of MapView
loggerService.Info($"New view pins: {MapView.Pins.Count}");
Task.Factory.StartNew(() =>
{
Task.Delay(2000).Wait();
loggerService.Info($"View pins: {MapView.Pins.Count}");
});
MapView.Refresh();
}
extra code with log is for debugging purposes - it shows correct values (prev pins = 7, new pins = 0, view pins = 0).
I've added history view without Zoom buttons to make sure it is changing - and it is (so there is no need to post XAML, I think).
How should I change MapView to hide pins when navigating to history and show them when I switch back to default view?
Or is there a better way to 'group' Pins and hide/show them basing on name?
Update
I think this may be important to mention:
public Pin AddMarker(Position point)
{
var pin = new Pin(MapView)
{
Label = "PinType.Pin",
Position = point,
Type = PinType.Pin,
Transparency = 0.5f,
Color = Xamarin.Forms.Color.FromRgb(2, 144, 210),
Scale = 0.5f,
};
MapView.Pins.Add(pin);
return pin;
}
This is the way I add Pins to view. This is the same MapView as above (all the code is from MapService).
Some options
Perhaps you can bind the IsVisible property of the Pins to a HistoryMode field on your view. I never used this myself.
Use some of the lower level functionality. Like
Use separate layers for default and history and enable/disable them when needed. You might need to do the conversion of WGS84 (gps coordinates) to SphericalMercator. https://mapsui.com/api/Mapsui.Projection.SphericalMercator.html?q=sphericalmercator
Use a ThemeStyle. It has a method that can be use to specify any style you need (visible/color/symbol) based and the feature attributes. https://mapsui.com/api/Mapsui.Styles.Thematics.ThemeStyle.html?q=themestyle
For the lower level functionality you may need to the samples to get you on your way: https://mapsui.com/documentation/samples.html

C#, xamarin, binding many objects

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.

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)

WPF Navigation and Rotating Backgrounds

I'm working on an application, and I'm using the MVVM approach.
Basically, there are currently two Pages, and 1 MainWindow.
I switch between the pages using a Frame inside MainWindow.
In the main window, there are 2 buttons which are basically global and should show in all pages; x (exit) and settings.
This is basically my 'shell', as I decided to not use a window border.
The problem is I'd like each page to have a different background and this is where it gets complicated:
- Settings page: Grey background.
- Main Page: Rotating background color that changes according to a property.
The thing is the background is being set in the main window, because it should apply to the global area as well (the top, where the exit and settings buttons are).
I first set the background (in MainWindow) as bound to a property the represents the current page (the value is then being translated into a color hex code with the help of a converter).
All in all, this results in a case where the background changes when a page is changed, but not when the property inside MainPage changes. I can clearly understand why, but I have no idea how to solve it.
The possible solutions I came up with so far:
Somehow causing the binding in MainWindow to update/refresh when the property is changed in MainPage.
Changing the background manually from inside each of the pages. (Although doesn't it negate the idea of mvvm?)
Move the background into each of the pages and set it from there, while making the global buttons on top of the page (which could be a bad thing in case controls end up overlapping).
If so, what would be the best solution to this problem?
If you haven't already, I'd suggest you install some package via NuGet to make MVVM style development more enjoyable. I personally prefer MVVMLight which is... well, light, but it also packs lot's of helpful features.
To communicate between ViewModels, you have (at least) two possible approaches.
1) ViewModelLocator (not recommended)
ViewModelLocator is central place holding references to all of your viewmodels. You could add a property that is then used by all of the viewmodels to get/set the background.
....
x:Name="Main"
DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}">
....
<Grid Background="{Binding Background, Converter={StaticResource StringBrushConverter}}">
...
2) Messenger (recommended)
When ever property changes in your viewmodel(s) or method is executed, you could send a message that your MainViewModel is registered to listen to. Sending a message would be as easy as...
Messenger.Default.Send(new UpdateBackgroundMessage(new SolidColorBrush(Colors.Blue)));
And you'd register for this message in your MainViewModel's constructor:
Messenger.Default.Register<UpdateBackgroundMessage>(this, message =>
{
Background = message.Brush;
});
Actual message class would be:
public class UpdateBackgroundMessage : MessageBase
{
public UpdateBackgroundMessage(Brush brush)
{
Brush = brush;
}
public Brush Brush { get; set; }
}
I know I'm simplifying things here but I hope you got the idea. Both approaches are valid even if you decide not to use MVVMLight.
Edit:
Here's Git repo with example https://github.com/mikkoviitala/cross-viewmodel-communication
I think you should use Application Properties for storing background. There are various benefit of this :
1) Globally available
2) Easy to remember or store user preference
3) Automatically maintain separate profile for each user as it store values in AppData folder of user.
you can use Messenger to notify that background property has changed so that main window or shell could pull out new background value and update it.

Categories