How to access a custom StackLayout from CodeBehind in Xamarin.Forms? - c#

I implemented a custom StackLayout in my Xamarin.Forms project to make the background of a ContentPage a Gradient, and I want to change the gradient flow in the code behind depending on the OS of the device. The problem is that when I assign a value to x:Name I can't find it in the xaml.cs class, therefore I cant access to its properties. Here goes the code:
Custom StackLayout code:
namespace MyProject.Renderers
{
public class GradientLayout : StackLayout
{
public string ColorsList { get; set; }
public Color[] Colors
{
get
{
string[] hex = ColorsList.Split(',');
Color[] colors = new Color[hex.Length];
for (int i = 0; i < hex.Length; i++)
{
colors[i] = Color.FromHex(hex[i].Trim());
}
return colors;
}
}
public GradientColorStackMode Mode { get; set; }
}
}
xaml code:
<renderers:GradientLayout
x:Name="theGradient"
ColorsList="#D81BDE,#4847FF"
Mode="ToBottomLeft">
//Irrelevant content
</renderers:GradientLayout>
xaml.cs code:
public partial class LogInPage : ContentPage
{
public LogInPage()
{
InitializeComponent();
if (Device.RuntimePlatform == Device.iOS)
{
//theGradient do not appear
}
}
}
And that is it, if you need any more information I will provide it as soon as I see your request, thank you all for your time, have a good day.

Did you create custom renderer for IOS and Android like this thread? https://stackoverflow.com/a/49862125/10627299
If so, make sure you add the WidthRequest="200" HeightRequest="200" for your GradientLayout
<renderers:GradientLayout
ColorsList="#dd8f68,#a9a9a9,#3a3939"
Mode="ToBottomRight"
WidthRequest="200"
HeightRequest="200">
</renderers:GradientLayout>
Here is running screenshot.
If above code is still not worked. You can use Magic Gradients in your applcation https://github.com/mgierlasinski/MagicGradients

Related

Values of View don't update after changing data

I'm trying to build an application that collects information of dams and weather forecasts from api and shows the data to users.
As shown below, the main page is supposed to look similar for dam information and weather forcast information.
Blue print
So, I want to make a component for card, that I can reuse for the data. In other words, I want to put 1) mainCards into 2)mainPage, so that I can have a page full of cards. And then, I want to put the mainPage on top of 3)mainWindow.
I want to change the values inside of the purple cards when I click a menu button on the left side that says "Dam Info" and "Weather Info".
When I press the "Dam Info" button, I want the purple cards to fetch dam data from an API and show it on the screen. Same for "Weather Info" button.
The problem is, I can clearly see that the values that I binded to the text blocks in the cards change when I run the code and show them on console. But, the actual UI doesn't update the change.
INotifyProperty says that the view model I implement for the current view changes as I press a button. It also says that the values(that I binded to the textblocks for purple cards) in a view model for purple card components change when I press a button.
I implemented a base class that implemented INotifyPropertyChanged for all view models, and RelayCommand for all commands.
I assumed that with INotifyPropertyChanged, the view will automatically change the UI value, but it does not.
I already tried to set the mode for binding to Two Way , but it doesn't work.
I tried to set certain values in a view model for Main Page(page that contains a lot of purple card components) and then make the purple card view model to fetch and reflect it. But it doesn't work.
Please help me. I've been dealing with this problem for a week. I have not much time left to just struggle with changing views and their components. I'm new to MVVM pattern and WPF and completely lost.
xaml of Main window is shown below
<Grid>
<RadioButton Content="Dam Info"
Command="{Binding ChangeMainPageForDamCommand}"
CommandParameter="Dam"/>
<RadioButton Content="Weather Info"
Command="{Binding ChangeMainPageForWeatherCommand}"
CommandParameter="Weather"/>
<RadioButton Content="My data"
Command="{Binding DetailPageCommand}"/>
<ContentControl Content="{Binding CurrentView,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
view model for main window is shown below
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
public RelayCommand MainPageCommand { get; set; }
public RelayCommand DetailPageCommand { get; set; }
public RelayCommand ChangeMainPageForDamCommand { get; set; }
public RelayCommand ChangeMainPageForWeatherCommand { get; set; }
public MainPageViewModel DamMainPageVM { get; set; }
public MainPageViewModel WeatherMainPageVM { get; set; }
public MainPageViewModel MainPageVM { get; set; }
public DetailPageViewModel DetailPageVM { get; set; }
public MainWindowViewModel()
{
MainPageVM = new MainPageViewModel("Dam");
CurrentView = MainPageVM;
DetailPageVM = new DetailPageViewModel();
ChangeMainPageForDamCommand = new RelayCommand(o =>
{
CurrentView = null;
DamMainPageVM = new MainPageViewModel("Dam");
CurrentView = DamMainPageVM;
Debug.WriteLine("ChangeMainPageFor Dam Command executed");
});
ChangeMainPageForWeatherCommand = new RelayCommand(o =>
{
WeatherMainPageVM = new MainPageViewModel("Weather");
CurrentView = WeatherMainPageVM;
Debug.WriteLine("ChangeMainPageFor Weather Command executed");
});
DetailPageCommand = new RelayCommand(o =>
{
CurrentView = DetailPageVM;
});
}
xaml for mainPage that contains cards is shown below
<Grid>
<components:MainCard/>
<components:MainCard/>
<components:MainCard/>
<components:MainCard/>
<components:MainCard/>
<components:MainCard/>
</Grid>
view model for the main page is shown below
public MainCardDatum MainCardDatumItem = new MainCardDatum();
public RelayCommand GetDamDetailCommand { get; set; }
private MainCardViewModel _mainCardViewModel;
public MainCardViewModel MainCardViewModel
{
get
{
return _mainCardViewModel;
}
set
{
_mainCardViewModel = value;
OnPropertyChanged(nameof(MainCardViewModel));
}
}
public MainPageViewModel() { }
public MainPageViewModel(string pageName)
{
var d = new MainCardViewModel(pageName);
GetMainPage(pageName);
Debug.WriteLine(pageName+" is ON");
}
public void GetMainPage(string pageName)
{
GenerateMainCard(pageName);
Debug.WriteLine(pageName+" made the page.");
}
private void GenerateMainCard(string pageName)
{
if (pageName == "Dam")
{
MainCardDatumItem.Id = 1;
MainCardDatumItem.MainCardName = "damDummyInfoName";
MainCardDatumItem.MainCardOption = "damDummyInfoOption";
}
else if (pageName == "Weather")
{
MainCardDatumItem.Id = 1;
MainCardDatumItem.MainCardName = "weatherDummyInfoName";
MainCardDatumItem.MainCardOption = "weatherDummyInfoOption";
}
else
{
Debug.Write("What?");
}
}
Main Card(Purple card component) xaml is below
<Grid>
<StackPanel>
<TextBlock Name="nameHehe"
Text="{Binding MainCardName,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Name="optionHehe"
Text="{Binding MainCardOption}"/>
</StackPanel>
<Button Background="Transparent"
Command="{Binding GetDamDetailCommand}"/>
</Grid>
Finally, the view model for the main card(purple card component) is below
public MainPageViewModel MainPageVM = new MainPageViewModel();
private string _mainCardName;
public string MainCardName
{
get
{
return _mainCardName;
}
set
{
_mainCardName = value;
OnPropertyChanged(nameof(MainCardName));
}
}
private string _mainCardOption;
public string MainCardOption
{
get
{
return _mainCardOption;
}
set
{
_mainCardOption = value;
OnPropertyChanged(nameof(MainCardOption));
}
}
public RelayCommand ClickCommand { get; set; }
public RelayCommand GetDamDetailCommand { get; set; }
public RelayCommand GetWeatherDetailCommand { get; set; }
public MainCardViewModel()
{
GenerateCardsForDam();
GetDamDetailCommand = new RelayCommand(o =>
{
MainCardName = "changed1";
MainCardOption = "changed2";
});
}
public MainCardViewModel(string pageName)
{
if (pageName == "Dam")
GenerateCardsForDam();
else
GenerateCardsForWeather();
GetDamDetailCommand = new RelayCommand(o =>
{
GenerateCardsForWeather();
});
}
public void GenerateCardsForDam()
{
MainCardName = "DamName1";
MainCardOption = "DamOption1";
Debug.WriteLine("GenerateCardsForDam executed");
}
public void GenerateCardsForWeather()
{
MainCardName = "WeatherName1";
MainCardOption = "WeatherOption1";
Debug.WriteLine("GenerateCardsForWeather executed");
}
I deleted out all unnecessary codes that are not related to this problem in xaml.
What am I doing wrong? Please help.
Also, it would be wonderful to know any discord server that I can ask and answer questions related to programming. I'm having a hard time to explain this problem in text tbh. Please let me know if you know one.
Thank you so much for reading so far. Sorry for messy explanation.
https://github.com/yk170901/dnw.git
Above is my Github repository for the project. I tried to add it correctly, but somehow the project folder become empty when I try to upload it. So I added a zip, I hope you can see the problem with the zip file. Sorry for the inconvenience.
You defined a DataTemplate for MainPage (a UserControl) in App.xaml.
<DataTemplate DataType="{x:Type viewModel:MainPageViewModel}">
<view:MainPage/>
</DataTemplate>
When this DataTemplate is applied, it is expected to instantiate a MainPage and set the instance of MainPageViewModel to MainPage's DataContext.
However, you added a code block shown below in MainPage.xaml.
<UserControl.DataContext>
<viewModels:MainPageViewModel/>
</UserControl.DataContext>
This means that when a MainPage is instantiated, an instance of MainPageViewModel will be instantiated as well and that instance will be set to MainPage's DataContext. This seems to prevent the DataTemplate from working as expected.
So, the solution is to remove that code block in MainPage.xaml.
In addition, I found that the appearace of MainPage will be the same regardless of the instance of MainPageViewModel and so you will not be able to check the difference from its appearance.

MVVMCross How to display a view within a view

I'm a new to the MVVMCross package, and C# for that matter. I've spent the better part of the day trying to figure out what I'm not understanding reading the documentation on presenters and navigation, etc. in order to try to understand, but I'm missing something.
I originally created a WPF app not implementing MVVM and now I wanted to convert, but I'm struggling with this part. I want to have a Main Menu that is part of a grid in a "MainWindow" like shell where the remaining portion of the page (and grid column 2) are used to display a nested view.
Ultimately, I’m just trying to reproduce the same layered controls in the original WPF application. In that app there is a content control Which takes up most of the form whose content property is set to a different form depending on the users selection.
MainWindow.xaml.cs
public partial class MainWindow : MvxWindow
{
public MainWindow(IMvxNavigationService navService)
{
InitializeComponent();
DataContext = new MainViewModel(navService);
//content.Content = new AdminMenuView();
}
}
MainViewModel.cs
private MvxViewModel _nextMenuContent;
public MainViewModel(IMvxNavigationService navService)
{
_navService = navService;
MoveMenuCommand = new MvxCommand(MoveMenu);
ChildViewModel = new AdminMenuViewModel();
GoToAdminMenu = new MvxCommand(SelectAdminMenu);
}
MainView.xaml
<ContentControl Content="{Binding ChildViewModel}"/>
***The grid and columns are all working fine
MainView.xaml.cs
public partial class MainView : MvxWpfView
{
public MainView()
{
InitializeComponent();
}
}
AdminMenuModel.cs
public class AdminMenuViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navService;
public AdminMenuViewModel()
{
Initialize();
}
public override void Prepare()
{
base.Prepare();
}
public override async Task Initialize()
{
await base.Initialize();
}
}
AdminMenuModel.xaml.cs
public partial class AdminMenuView : MvxWpfView
{
public AdminMenuView()
{
InitializeComponent();
}
public new AdminMenuViewModel ViewModel
{
get { return base.ViewModel as AdminMenuViewModel; }
set { base.ViewModel = value; }
}
}
When I call the AdminMenuViewModel it runs, but all I get in the content control is either a blank screen if I Bind the "ChildViewModel" to the DataContext property of the content control and a string of the path to the AdminMenuViewModel if I bind it to the content property.
You have to set MainViewModel as DataContext of your main window
public MainWindow(IMvxNavigationService navService)
{
DataContext = new MainViewModel(navService);
InitializeComponent();
}

UWP MVVM binding to TextBox and passing back value

I am trying to get the content of a TextBox updated using Binding in a MVVM environment. When a Button receive focus, it passes a value, and that value should be reflected in the TextBox. I seem to have the first part right, however seems to be struggling at passing the value..
I know the question about MVVM has been asked before (including by myself), but I really cannot get it, for some reasons..
So I start with my model:
public class iText : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
I then continue with my ViewModel:
private iText _helper = new iText();
public iText Helper
{
get { return _helper; }
set
{
_helper = value;
}
}
The XAML page:
<Page.Resources>
<scan:ModelDataContext x:Key="ModelDataContext" x:Name="ModelDataContext"/>
</Page.Resources>
<TextBox Text="{Binding Helper.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
I then try to update the Text from MainPage.cs
public sealed partial class MainPage : Page
{
public MainPageViewModel iText { get; set; }
public MainPage()
{
InitializeComponent();
iText = new MainPageViewModel();
}
private void btn_GotFocus(object sender, RoutedEventArgs e)
{
var str = "test"
iText.Helper.Text = str;
}
I could really appreciate if someone could tell me what I do wrong, and where. Thanks so much in advance.
In your MainPage constructor, try setting the datacontext to your ViewModel.
Something like...
public MainPage()
{
InitializeComponent();
iText = new MainPageViewModel();
this.dataContext = iText;
}

Trouble binding to a textblock - Windows Phone - MVVM

I have spent a couple hours trying to figure out this ONE problem. Here's what is happening:
I am trying to bind a Title to my XAML file from my ViewModel. All of the code executes (I checked using breakpoints/watch), but the binding doesn't actually work. I am very new to development and especially MVVM, so I am having a hard time figuring this out. Relevant code:
App.Xaml.Cs
private static MainPageViewModel _mainPageViewModel = null;
public static MainPageViewModel MainPageViewModel
{
get
{
if (_mainPageViewModel == null)
{
_mainPageViewModel = new MainPageViewModel();
}
return _mainPageViewModel;
}
}
MainPageModel
public class MainPageModel : BaseModel
{
private string _pageTitle;
public string PageTitle
{
get { return _pageTitle; }
set
{
if (_pageTitle != value)
{
NotifyPropertyChanging();
_pageTitle = value;
NotifyPropertyChanged();
}
}
}
MainPageViewModel
private void LoadAll()
{
var page = new MainPageModel();
page.PageTitle = "title";
MainPageViewModel
public MainPageViewModel()
{
LoadAll();
}
MainPage.Xaml.Cs
public MainPage()
{
InitializeComponent();
DataContext = App.MainPageViewModel;
}
MainPage.Xaml
<Grid x:Name="LayoutRoot">
<phone:Panorama Title="{Binding PageTitle}">
Do I need a using statement in the Xaml too? I thought I just needed to set the data context in the MainPage.Xaml.Cs file.
I'm pretty sure I've posted all of the relevant code for this. Thanks everyone!!
The problem is here, in the view model class:
private void LoadAll()
{
var page = new MainPageModel();
page.PageTitle = "title";
All you've done here is create a local object "page" -- this will not be accessible anywhere outside the local scope. I suppose what you meant to do is make "page" a member of "MainPageViewModel":
public class MainPageViewModel
{
public MainPageModel Model { get; private set; }
private void LoadAll()
{
_page = new MainPageModel();
_page.PageTitle = "title";
}
}
This way, you'll be able to bind to the "PageTitle" property -- but remember, it's a nested property, so you'll need:
<phone:Panorama Title="{Binding Model.PageTitle}">

c# why cannot ListBoxItem display each picture (left side)?

I have a single ListBox that is supposed to display a picture with each item. I wrote codes and when I run it, pictures couldn't be displayed, but only text displayed. What have I done wrong in my codes? I made sure the image file path is correct.
I want to display each item with text (right side) and icon (left side).
WPF:
<ListBox Name="ListTest" DisplayMemberPath="Name" HorizontalAlignment="Left" Height="358" Margin="603,38,0,0" VerticalAlignment="Top" Width="361">
</ListBox>
C#
public partial class UserControl2 : UserControl
{
public UserControl2()
{
InitializeComponent();
this.LoadLogos();
}
private void LoadLogos()
{
this.ListTest.Items.Add(new CompanyDataContext("Adobe", "Adobe is a designing tool.", "/Company Logos/testDinner.jpg"));
this.ListTest.Items.Add(new CompanyDataContext("Facebook", "FedEx is a social networking website.", "/Company Logos/facebook.jpg"));
this.ListTest.Items.Add(new CompanyDataContext("FedEx", "FedEx is a courier company.", "/Company Logos/fedex.jpg"));
}
private class CompanyDataContext
{
public CompanyDataContext(string name, string about, string image)
{
this.Name = name;
this.About = about;
this.Image = image;
}
public string Name { get; private set; }
public string About { get; private set; }
public string Image { get; private set; }
}
}
You need a DataTemplate for CompanyDataContext as it does not inherit from Visual, WPF has no idea how to render it hence it calls the ToString Method on this.
This can be dealt with aDataTemplate for the ListBox
Untested template:
<ListBox.ItemTemplate>
            <DataTemplate>
                <Border x:Name="bord" CornerRadius="5" Margin="2" BorderBrush="LightGray" BorderThickness="3" Background="DarkGray">
                    <StackPanel Margin="5">
                        <TextBlock x:Name="txt" Text="{Binding Name}" FontWeight="Bold"/>
                        <Image Source="{Binding Image}" Height="100"/>
                    </StackPanel>
                </Border>
            </DataTemplate>
</ListBox.ItemTemplate>
Edited typo

Categories