Trouble Binding class to grid, second place - c#

So I'm trying to create my own music player.
I have a Listview updating properly with current playlist.
My problem begins when I want a grid to show the song which is currently playing.
When binding to those strings they all return Null.
From Class:
public class Song : INotifyPropertyChanged
{
public string Title { get; set; }
public string Path { get; set; }
public string Artist { get; set; }
public int Time { get; set; }
public string Album { get; set; }
//public ImageBrush Portrait { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public Song(string title, string path, string artist, int time, string album)//, ImageBrush portrait)
{
this.Path = path;
this.Title = title;
this.Artist = artist;
this.Time = time;
this.Album = album;
//this.Portrait = portrait;
}
public string SongCurrentPlaying
{
get { return Title; }
set
{
Title = value;
OnPropertyChanged("SongCurrentPlaying");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(Title));
}
}
From XAML:
<Grid HorizontalAlignment="Left" Height="143" VerticalAlignment="Top" Width="276">
<Image HorizontalAlignment="Left" Height="124" Margin="10,10,0,0" VerticalAlignment="Top" Width="134" Stretch="Fill"/>
<TextBlock HorizontalAlignment="Left" Margin="144,100,0,0" TextWrapping="Wrap" Text="{Binding Songs.Title}" VerticalAlignment="Top" Height="25" Width="122"/>
<TextBlock HorizontalAlignment="Left" Margin="144,40,0,0" TextWrapping="Wrap" Text="{Binding SongCurrentPlaying.Title, PresentationTraceSources.TraceLevel=High}" VerticalAlignment="Top" Height="25" Width="122"/>
<TextBlock HorizontalAlignment="Left" Margin="144,70,0,0" TextWrapping="Wrap" Text="{Binding Song.Title}" VerticalAlignment="Top" Height="25" Width="122"/>
<TextBlock HorizontalAlignment="Left" Margin="144,10,0,0" TextWrapping="Wrap" Text="{Binding Songs.Title}" VerticalAlignment="Top" Height="25" Width="122"/>
</Grid>
As you can see I've been experimenting on different bindings to get values from Title as example, none has been successful.

Your class structure does not make much sense. You have a Song class that holds a Song's information. However, that class also holds SongCurrentPlaying. Now, if you mean to have SongCurentPlaying on the same class as Song then I would narrow down your issue to how you have performed the XAML bindings. I would assume that the DataContext of your control has not been set to an instance of Song. e.g.
public partial class MainWindow : Window
{
public MainWindow()
{
var someSong = new Song(...);
this.DataContext = someSong;
InitializeComponent();
}
}
Once set as the DataContext you may then bind to the class' properties directly (e.g. {Binding Title}).

Your song class does not need to implement INotifyPropertyChanged. You should think of your songs as models. Instead you should be making a viewmodel for your music player that will hold all your information for your view such as in this case CurrentSongPlaying.
I would make a viewmodel that looks like this
internal class SongPlayerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Song _songPlaying;
public Song SongPlaying
{
get { return _songPlaying; }
set {
_songPlaying = value;
OnPropertyChanged("SongPlaying");
}
}
protected void OnPropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Then make sure to set the datacontext to an instance of this in the view and inside of the xaml just bind the the title with code like this.
datacontext setup
private readonly SongPlayerViewModel _viewModel = new SongPlayerViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _viewModel;
}
example xaml binding for current song playing
<TextBlock text="{Binding SongPlaying.Title}"/>
If you want something when no song is playing look into Fallbackvalue

Related

My UserControl's TextBlock binding doesn't update even once

I know this has been asked for many times. I read a lot of them and tried different ways but still could not get it to work.
The xaml code is a UserControl:
<Grid Name="middle">
<d:TextBlock Text="{x:Bind LayerNodeData.CleanName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Foreground="WhiteSmoke" FontSize="12" FontFamily="Arial" VerticalAlignment="Center" RelativePanel.RightOf="visibleUI" DoubleTapped="OnEditNameBegin" />
</Grid>
I set both this.DataContext and the Grid's DataContext to the data instance.
c#
public ucLayerRow(ImageLayerNode data)
{
LayerNodeData = data;
DataContext = LayerNodeData;
this.InitializeComponent();
middle.DataContext = LayerNodeData;
LayerNodeData.NotifyPropertyChanged("CleanName"); // test if it work
RefreshUI();
}
Model class
public partial class ImageLayerNode : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
// PropertyChanged is always null.
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public string mCleanName = string.Empty;
public string CleanName {
get => mCleanName;
set { mCleanName = value; NotifyPropertyChanged();}
}
....
}
I tried add a breakpoint to the PropertyChanged and found that it is always null and thus never get called. I also tried changing the mode to OneWay, TwoWays but still nothing.
The textblock is away empty not even getting a value once.
The user control is added like this to the main page. Not sure if it is related.
var rowUI = new ucLayerRow(layerNode);
layerContainer.Children.Add(rowUI);
My UserControl's TextBlock binding doesn't update even once
During the testing, the problem looks that you use design time for usercontrol. <d:TextBlock/> please remove d: and make your usercontrol like the following.
Xaml
<Grid>
<TextBlock
VerticalAlignment="Center"
FontFamily="Arial"
FontSize="12"
Foreground="Red"
Text="{x:Bind LayerNodeData.CleanName, Mode=OneWay}" />
</Grid>
Code behind
public sealed partial class ucLayerRow : UserControl
{
public ucLayerRow(ImageLayerNode data)
{
this.InitializeComponent();
LayerNodeData = data;
}
public ImageLayerNode LayerNodeData { get; set; }
}
public partial class ImageLayerNode : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
// PropertyChanged is always null.
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private string mCleanName = string.Empty;
public string CleanName
{
get => mCleanName;
set { mCleanName = value; NotifyPropertyChanged(); }
}
}

C#/WPF Retrieve textbox input value

In my app, I am trying to get the input value of a textbox for use later but I'm unable to successfully get the value. Any ideas where I have made a mistake or how to fix it? Below is my code, if more code is needed, feel free to ask. Thank you in advance.
View Model:
namespace Creator.ViewModels
{
public class CreatureCreatorViewModel : ViewModelBase, INotifyPropertyChanged
{
public CreatureModel CreateNewCreature { get; set; }
/*Variables to "get" input data*/
private string creatureName;
public string CreatureName
{
get {
return creatureName;
}
set
{
if(!string.Equals(creatureName, value))
{
creatureName = value;
OnPropertyChanged("CreatureName");
}
}
}
public CreatureCreatorViewModel(NavigationStore navigationStore)
{
CreateNewCreature = new CreatureModel().NewCreature(creatureName);
}
}
}
View:
<TextBox x:Name="CreatureNameBox" Text="{Binding Path=CreatureName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" TextWrapping="NoWrap" FontFamily="Century Gothic" FontSize="16" Margin="150,0,150,16" MaxLines="1" Height="26" HorizontalAlignment="Stretch" VerticalAlignment="Center" Padding="5,3,5,3" MaxLength="100"/>
ViewModelBase:
namespace Creator.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public virtual void Dispose() { }
}
}
I did a simple test using the source code you provided. And I could see that it's normal as shown in the image below.
I put the sample on GitHub to compare with your source code.
Here
<TextBox Text="{Binding Path=CreatureName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<TextBlock Text="{Binding CreatureName}"/>

How to update TextBlock text inside of ListBox item

So I have a simple UDP chat app from a WinForm project, which I wanted to look a little bit better, so I am re-making it in WPF. As I realized I can easily put 2 or more TextBlocks inside of a ListItem, I wanted to display the last message of each chat, like so:
But I have no Idea on how to edit those TextBlocks :( I literary just started with WPF, so I bet I just made a duplicate, but because of that, I don't even know how to search for this issue.
Here is the custom ListBox:
<ListBox x:Name="myList" HorizontalAlignment="Left" Width="264" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0,1,1,0" MouseLeftButtonUp="myList_MouseLeftButtonUp" Margin="0,25,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="0,0,0,1" Width="250">
<DockPanel Margin="0,7">
<Ellipse Name="ellipse" Margin="5" DockPanel.Dock="Left" Style="{DynamicResource elstyle}">
</Ellipse>
<TextBlock Text="{Binding Name}" DockPanel.Dock="Top" Margin="0,0,0,7" FontWeight="Bold" MaxWidth="250"></TextBlock>
<TextBlock Text="{Binding ID}" DockPanel.Dock="Top" Visibility="Hidden" FontSize="1.333"></TextBlock>
<TextBlock x:Name="last_message" Text="{Binding LastMessage}" DockPanel.Dock="Bottom" MaxWidth="250"></TextBlock>
</DockPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is simplified model to show the principal but if you would create view model class that implement INotifyPropertyChanged interface to hold your item data
public class MyItem : INotifyPropertyChanged
{
private string _name;
private string _id;
private string _lastMessage;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string ID
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("ID");
}
}
public string LastMessage
{
get { return _lastMessage; }
set
{
_lastMessage = value;
OnPropertyChanged("LastMessage");
}
}
}
and then in your window
public partial class MainWindow : Window
{
private readonly ObservableCollection<MyItem> _myItems = new ObservableCollection<MyItem>();
public MainWindow()
{
InitializeComponent();
myList.ItemsSource = _myItems;
_myItems.Add(new MyItem { Name = "name", ID = "id", LastMessage = "last message" });
_myItems[0].LastMessage = "new message";
}
}
and then you don't operate on myList control anymore but on _myItems list and its items. If you add/remove item in the collection it will add/remove item in the UI, if you change property of an item it will update bound property in the UI

Bind INotifyPropertyChanged class as Property in another class

I have a class that has a fairly long running process that I want the GUI to give progress on.
The class has a property called Progress that is a class implementing INotifyPropertyChanged. I'm using a BusyWorker and bind the class's Progress property to it's datacontext, but whenever the progress changes the BusyWorker does not show anything. I don't know if I'm making any sense here, so here's some code:
The class in question:
public class MyClass
{
public Progress MyProgress { get; set; }
public void Run()
{
MyProgress = new Progress();
MyProgress.Status = "Initialising";
// Do stuff, update progress, etc.
}
}
public class Progress : INotifyPropertyChanged
{
private string status;
public string Status
{
get { return status; }
set
{
status = value;
OnPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
XAML:
// ...
<xctk:BusyIndicator HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="busyIndicator" VerticalAlignment="Stretch" BusyContent="{Binding}">
<xctk:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="{Binding Status}" FontWeight="Bold" HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
</xctk:BusyIndicator.BusyContentTemplate>
</xctk:BusyIndicator>
// ...
XAML.CS:
MyClass test = new MyClass();
BusyIndicator.DataContext = test.MyProgress;
BusyIndicator.IsBusy = true;
test.Run();
If I run it like this, and stop at the OnPropertyChanged call, PropertChanged is always null. If I make a separate Progress object in my xaml.cs it works just fine, but I want my 'Run' method to handle this. Is this even possible?
The Problem is you are assigning Data Context before calling the run method which means by the time you are assigning data context "MyProgress" Object is "Null".. so data context is null before calling the "Run" method.. you are calling the Run method which creates an instance for "MyProgress" but since your "MyClass" is not "INotifyPropertyChanged" its not able to notify the data context change...
Solution is: Try creating MyProgress instance in the constructor of MyClass.. so by the time of assigning data context will not be null and the in the run method don't create any instance just update the status property..
Something like this
public class MyClass
{
public Progress MyProgress { get; set; }
public MyClass()
{
MyProgress = new Progress();
}
public void Run()
{
MyProgress.Status = "Initialising";
// Do stuff, update progress, etc.
}
}
<xctk:BusyIndicator HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="busyIndicator" VerticalAlignment="Stretch" BusyContent="{Binding}">
<xctk:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="{Binding Path=Status}" FontWeight="Bold" HorizontalAlignment="Left" DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=BusyIndicator}}"/>
</StackPanel>
</DataTemplate>
</xctk:BusyIndicator.BusyContentTemplate>
// ...
This works for me:
*.xaml
<xctk:BusyIndicator HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="busyIndicator" VerticalAlignment="Stretch" IsBusy="True">
<xctk:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="{Binding Path=Test.MyProgress.Status, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" FontWeight="Bold" HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
</xctk:BusyIndicator.BusyContentTemplate>
</xctk:BusyIndicator>
*.xaml.cs:
public partial class MainWindow : Window
{
public MyClass Test { get; set; }
public MainWindow()
{
Test = new MyClass();
InitializeComponent();
Test.Run();
}
}
public class MyClass
{
public Progress MyProgress { get; set; }
public void Run()
{
MyProgress = new Progress();
MyProgress.Status = "Initialising";
// Do stuff, update progress, etc.
}
}
public class Progress : INotifyPropertyChanged
{
private string status;
public string Status
{
get { return status; }
set
{
status = value;
OnPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}

Windows Phone 7: Making ListBox items change dynamically

I am working on creating a Windows Phone app that will play a series of sound clips selected from a list. I am using the MVVM (Model View View-Model) Design pattern and have designed a model for my data, along with a view model for my page. Here is what the XAML for the ListBox looks like:
<ListBox x:Name="MediaListBox" Margin="0,0,-12,0" ItemsSource="{Binding Media}" SelectionChanged="MediaListBox_SelectionChanged" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ListBox.ItemTemplate >
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Orientation="Horizontal">
<Image Source="../Media/Images/play.png" />
<StackPanel >
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding ShortDescription}" TextWrapping="Wrap" Margin="12,-6,12,0" Visibility="{Binding ShortDescriptionVisibility}" Style="{StaticResource PhoneTextSubtleStyle}"/>
<TextBlock Text="{Binding LongDescription}" TextWrapping="Wrap" Visibility="{Binding LongDescriptionVisibility}" />
<StackPanel>
<Slider HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Visibility="{Binding LongDescriptionVisibility}" ValueChanged="Slider_ValueChanged" LargeChange="0.25" SmallChange="0.05" />
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My question is this: I want to be able to expand and collapse part of the items in the ListBox. As you can see, I have a binding for the visibility. That binding is coming from the MediaModel. However, when I change this property in the ObservableCollection, the page is not updated to reflect this.
The ViewModel for this page looks like this:
public class ListenPageViewModel : INotifyPropertyChanged
{
public ListenPageViewModel()
{
this.Media = new ObservableCollection<MediaModel>;
}
/// <summary>
/// A collection for MediaModel objects.
/// </summary>
public ObservableCollection<MediaModel> Media { get; private set; }
public bool IsDataLoaded { get; private set; }
/// <summary>
/// Creates and adds the media to their respective collections.
/// </summary>
public void LoadData()
{
this.Media.Clear();
this.Media.Add(new MediaModel()
{
Title = "Media 1",
ShortDescription = "Short here.",
LongDescription = "Long here.",
MediaSource = "/Media/test.mp3",
LongDescriptionVisibility = Visibility.Collapsed,
ShortDescriptionVisibility = Visibility.Visible
});
this.Media.Add(new MediaModel()
{
Title = "Media 2",
ShortDescription = "Short here.",
LongDescription = "Long here.",
MediaSource = "/Media/test2.mp3",
LongDescriptionVisibility = Visibility.Collapsed,
ShortDescriptionVisibility = Visibility.Visible
});
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The bindings work correctly and I am seeing the data displayed; however, when I change the properties, the list does not update. I believe that this may be because when I change things inside the observable collection, the property changed event is not firing.
What can I do to remedy this? I have poked around for some info on this, but many of the tutorials don't cover this kind of behavior. Any help would be greatly appreciated!
Thanks
Edit: As requested, I have added the MediaModel code:
public class MediaModel : INotifyPropertyChanged
{
public string Title { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public string MediaSource { get; set; }
public Visibility LongDescriptionVisibility { get; set; }
public Visibility ShortDescriptionVisibility { get; set; }
public MediaModel()
{
}
public MediaModel(string Title, string ShortDescription, string LongDescription, string MediaSource, Visibility LongDescriptionVisibility, Visibility ShortDescriptionVisibility)
{
this.Title = Title;
this.ShortDescription = ShortDescription;
this.LongDescription = LongDescription;
this.MediaSource = MediaSource;
this.LongDescriptionVisibility = LongDescriptionVisibility;
this.ShortDescriptionVisibility = ShortDescriptionVisibility;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Originally, I did not have this class implement the INotifyPropertyChanged. I did this to see if it would solve the problem. I was hoping this could just be a data object.
Try changing your long description visibility property to this to see if that fixes it
private Visibility _longDescriptionVisibility;
public Visibility LongDescriptionVisibility
{
get { return _longDescriptionVisibility; }
set
{
_longDescriptionVisibility = value;
NotifyPropertyChanged("LongDescriptionVisibility");
}
}
If it does make the same change to the short description property.

Categories

Resources