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

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

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.

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

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

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;
}

why do it show a black screen when i use Uri class?

I'm creating a simple windows store app in c#. When i'm binding my image control to display an image from code- behind, my screen goes black. Anyone know how i can solve the problem?
ImageService Class
public class ImageService
{
public Image Image { get; set; }
public ImageService()
{
var uri = new System.Uri("ms-appx:///assets/Logo.scale-100.png");
var bmp = new BitmapImage(uri);
Image.Source = bmp;
}
}
XAML file
<Image x:Name="image" HorizontalAlignment="Left" Height="223" Margin="394,279,0,0" VerticalAlignment="Top" Width="305" Source="{Binding Image, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Stretch="UniformToFill"/>
Use this:
public class ImageService
{
public Uri Image { get; set; }
public ImageService()
{
Image = new Uri("ms-appx:///assets/Logo.scale-100.png");
}
}
The Source property of an Image is of type ImageSource which can be easily replaced by a Uri. (MSDN).
Images in XAML have a built in converter so you can just bind to a Uri, you don't have to create an Image in the service.
Your service isn't implementing INotifyPropertyChanged so if you set your image in your service outside the constructor your view won't update.
I don't see in your code where you are instantiating your Image. Image will be null so when your view loads, the Image will be null resulting in a blank image on your view.
You mean like this? Cause it still makes the sceen go black.
public class ImageService : INotifyPropertyChanged
{
private Uri _uri;
public Uri Uri
{
get { return _uri; }
set
{
_uri = value;
OnPropertyChanged();
}
}
public ImageService()
{
Uri = new Uri("ms-appx///assets/Logo.scale-100.png");
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Using ListBox to navigate a TextBox with MVVM

I am trying to move the following function listView_SelectionChanged away from code-behind and handle it directly inside my ViewModel (or directly as XAML). And I was hoping that someone might have a better idea on how to implement this.
The TextBox contains Sections e.g. [Secion1] and to help navigate I have a ListBox on the side of the TextBox that contains a list of all Sections. If you click on one of the Sections it will automatically jump to that part of the Text.
The code currently looks something like this:
XAML
ListBox ItemsSource="{Binding Path=Sections}" Name="listBox"
SelectionMode="Single" Width="170"
DisplayMemberPath="Section"
SelectionChanged="listView_SelectionChanged"/>
<TextBox Name="TextBox1" Text="{Binding Path=Source}"/>
Model
public class SourceData
{
public SourceData()
{
Sections = new List<SectionData>();
}
public String Source { get; set; }
public List<SectionData> Sections { get; set; }
}
public class SectionData
{
public int Line { get; set; } // Line of the Section
public String Section { get; set; } // Section name (e.g. [Section1]
}
Code-behind
private void listView_SelectionChanged(object sender,
System.Windows.Controls.SelectionChangedEventArgs e)
{
var test = (SectionData)listBox.SelectedItem; // Get Information on the Section
if (test.Line > 0 && test.Line <= TextBox1.LineCount) // Validate
{
TextBox1.ScrollToLine(test.Line - 1); // Scroll to Line
}
}
In such situations I usually create an attached behavior (in your case it will be a behavior which will allow synchronizing textbox scrolled line), add property in ViewModel (SourceData) which will rule that attached behavior and bind behavior to this property.
Steps you should do in your case (I assume you know how to create an attached properties):
1) Create attached behavior ScrolledLine for textbox. It should support at least one-way binding. In attached property callback you will scroll textBox (to which behavior is attached) to the line. Below you will find a quick sample how to implement such a behavior.
2) Your SourceData should be extended with at least two properties: SelectedSection and ScrolledLine. ScrolledLine should be raising PropertyChanged. SelectedSection setter should change ScrolledLine:
private SectionData _selectedSection;
public SectionData SelectedSection
{
get { return _selectedSection; }
set
{
_selectedSection = value;
if (_selectedSection != null) SelectedLine = _selectedSection.Line;
}
}
3) Bind your view to these two new properties:
b below is xml-namespace for your attached behavior from #1
<ListBox ItemsSource="{Binding Path=Sections}" SelectionMode="Single" Width="170" DisplayMemberPath="Section" SelectedItem="{Binding SelectedSection, Mode=TwoWay}" />
<TextBox Text="{Binding Path=Source}" b:Behaviors.ScrolledLine="{Binding ScrolledLine}" />
4) Remove your listView_SelectionChanged event handler from view. Your view should not have any code except InitializeComponent from now on.
P.S.: Here is a sample how your attached behavior should look like:
public class b:Behaviors
{
#region Attached DP registration
public static int GetScrolledLine(TextBox obj)
{
return (int)obj.GetValue(ScrolledLineProperty);
}
public static void SetScrolledLine(TextBox obj, int value)
{
obj.SetValue(ScrolledLineProperty, value);
}
#endregion
public static readonly DependencyProperty ScrolledLineProperty=
DependencyProperty.RegisterAttached("ScrolledLine", typeof(int), typeof(Behaviors), new PropertyMetadata(ScrolledLine_Callback));
// This callback will be invoked when 'ScrolledLine' property will be changed. Here you should scroll a textbox
private static void ScrolledLine_Callback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var textbox = (TextBox) source;
int newLineValue = (int)e.NewValue;
if (newLineValue > 0 && newLineValue <= textBox.LineCount) // Validate
textbox.ScrollToLine(newLineValue - 1); // Scroll to Line
}
}

Categories