Problem
I want to refresh my wpf view when a change is made in a List of objects in my application, but it wont register the INotifyChanged method when I change a value.
What I've tried
I went to multiple different stackoverflow pages with sort of the same problem but I don't get it working right. It wont register a change in a object in the list.
my code
below is the code for the MainWindow of the WPF application in wher with the last button click I change the value of XLocation in an object out of a list.
public partial class MainWindow : Window
{
private string filePathArtist { get; set; }
private string filePathGrid { get; set; }
public Game.Game Game { get; set; }
public MainWindow()
{
InitializeComponent();
filePathGrid = String.Empty;
filePathArtist = String.Empty;
}
private void BtnOpen_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
bool? res = openFileDialog.ShowDialog();
if (res == true)
{
string filepathgrid = openFileDialog.FileName;
filePathGrid = filepathgrid;
GridTextBox.Text = filepathgrid;
}
}
private void PickArtistBtn_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
bool? res = openFileDialog.ShowDialog();
if (res == true)
{
string filepathartist = openFileDialog.FileName;
filePathArtist = filepathartist;
ArtistTextBox.Text = filepathartist;
}
}
private void CreateGridBtn_Click(object sender, RoutedEventArgs e)
{
Game = new Game.Game(filePathGrid, filePathArtist);
this.DataContext = Game;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Game.Artists[0].XLocation = 30;
}
}
Next code is the Game class where is implemented a INotfyPropertyChanged on the list of Artists.
public class Game : INotifyPropertyChanged
{
public List<Artist> _artists;
public List<Artist> Artists
{
get
{
return _artists;
}
set
{
_artists = value;
OnPropertyChanged("Artists");
}
}
public List<ITile> Tiles { get; set; }
public Game()
{
}
public Game(string graphPath, string artistPath)
{
IDataParser graphParser = DataFactory.DataFactory.Instance.CreateParser(graphPath);
IDataParser artistParser = DataFactory.DataFactory.Instance.CreateParser(artistPath);
Tiles = graphParser.ParseGridData(graphPath);
Artists = artistParser.ParseArtistData(artistPath);
Test = "new Game";
}
public string Test { get; set; } = "t";
public event PropertyChangedEventHandler? PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Ive also added the INotifyPropertyChanged in the Artists class
public class Artist : INotifyPropertyChanged
{
private float _xLocation;
private float _yLocation;
private int _xVelocity;
private int _yVelocity;
public float XLocation
{
get => _xLocation;
set
{
_xLocation = value;
OnPropertyChanged("XLocation");
}
}
public float ConvertedXLoc
{
get => XLocation * (float)3.75;
set { }
}
public float YLocation
{
get => _yLocation;
set
{
_yLocation = value;
OnPropertyChanged("YLocation");
}
}
public float ConvertedYLoc
{
get => YLocation * (float)3.75;
set { }
}
public int XVelocity
{
get => _xVelocity;
set
{
_xVelocity = value;
}
}
public int YVelocity
{
get => _yVelocity;
set
{
_yVelocity = value;
}
}
public event PropertyChangedEventHandler? PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then here is the Xaml code where I bind the objects to the wpf UI.
<Window x:Class="BroadwayBoogie.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:game="clr-namespace:BroadwayBoogie.Game"
mc:Ignorable="d"
Title="MainWindow" Height="900" Width="900"
>
<Window.DataContext>
<game:Game/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="201*"/>
<ColumnDefinition Width="199*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="21*"/>
<RowDefinition Height="401*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Button x:Name="BtnOpen" Content="Pick Grid" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Click="BtnOpen_Click"/>
<TextBox x:Name="GridTextBox" HorizontalAlignment="Center" TextWrapping="NoWrap" VerticalAlignment="Center" Width="266" />
<Button x:Name="PickArtistBtn" Content="Pick Artist" HorizontalAlignment="Left" Margin="356,0,0,0" VerticalAlignment="Center" Click="PickArtistBtn_Click" RenderTransformOrigin="-0.135,0.647"/>
<TextBox x:Name="ArtistTextBox" HorizontalAlignment="Left" Margin="30,14,0,0" TextWrapping="NoWrap" VerticalAlignment="Top" Width="231" Grid.Column="1"/>
<Button x:Name="CreateGridBtn" Grid.Column="1" Content="Create Grid" HorizontalAlignment="Left" Margin="311,14,0,0" VerticalAlignment="Top" Click="CreateGridBtn_Click"/>
<Canvas Width="800" Height="800" Grid.ColumnSpan="2" Margin="49,15,51,27" Grid.Row="1" Background="DarkSeaGreen" Grid.RowSpan="2">
<ItemsControl Name="tilesItemsControl" ItemsSource="{Binding Tiles}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<Rectangle
Width="15"
Height="15"
Fill="{Binding Color}"
Canvas.Left ="{Binding ConvertedXLoc}"
Canvas.Top="{Binding ConvertedYLoc}" />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Name="ArtistItemsControl" ItemsSource="{Binding Artists}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<Rectangle
Width="3.75"
Height="3.75"
Fill="Black"
Canvas.Left ="{Binding ConvertedXLoc}"
Canvas.Top="{Binding ConvertedYLoc}" />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
<Grid/>
<Button Content="Button" HorizontalAlignment="Left" Margin="4,0,0,0" Grid.Row="2" VerticalAlignment="Center" Click="Button_Click"/>
</Grid>
So with the press of the button I added for testing purposes. It changes a value in the List and then the PropertyChanged method should detect that but it doesn't detect it it just skips it.
Question
So my basic question is, how do I detect the change of a property from objects out of the List of Artists.
OnPropertyChanged will only be executed when the property itself is changed. A new item in a list is not a property change. That's the reason why no updates happens.
Instead a List, try an ObservableCollection. An ObservableCollection implements an additional INotifyCollectionChanged which makes the UI able to react on changing items in the list.
Related
In my Code does my Function OnPropertyChanged not update the UI, despite the Fact that it does work everywhere else in my Programm. I try to upgrade the Content of the Button, so that if the User presses it, it will change it's Content. It does Update the value but then won't show it in the UI for some weird Reason. Any help is appreciated...
The View:
public partial class CreateMP : Window
{
public CreateMP()
{
InitializeComponent();
}
public CreateMP(string UserName)
{
CreateMPViewModel.User = UserName;
InitializeComponent();
}
}
The ViewModel:
public class CreateMPViewModel : Window
{
private string _ButtonContent = "Create Server";
private bool _ButtonEnable = true;
public event PropertyChangedEventHandler PropertyChanged;
private readonly TcpListener listener;
private TcpClient client;
public ICommand ButtonCommand_Back { get; set; }
public ICommand ButtonCommand_CreateGame { get; set; }
public string ButtonContent
{
get
{
return _ButtonContent;
}
set
{
if (value != _ButtonContent)
{
_ButtonContent = value;
OnPropertyChanged("ButtonContent");
}
}
}
public bool ButtonEnable
{
get
{
return _ButtonEnable;
}
set
{
if (value != _ButtonEnable)
{
_ButtonEnable = value;
OnPropertyChanged("ButtonEnable");
}
}
}
public static string User { get; set; }
public string IPAdresse { get; set; }
public int Passwort { get; set; }
public CreateMPViewModel()
{
ButtonCommand_Back = new DelegateCommand(BackButtonClick);
ButtonCommand_CreateGame = new DelegateCommand(CreateGameClick);
int Port = 51246;
Random rnd = new Random();
Password = rnd.Next(0, 99999);
IPAddress localAdd = IPAddress.Parse(IPAdresse);
listener = new TcpListener(localAdd, Port);
listener.Start();
}
private void BackButtonClick()
{
client = GameHandler.CurrentClient;
Application.Current.MainWindow.Close();
if (client != null)
{
client.Close();
}
if (listener != null)
{
listener.Stop();
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void CreateGameClick()
{
ButtonEnable = false;
ButtonContent = "Waiting for Player...";
//More Code here after the Update of the Button
}
}
The XAML:
<Window x:Class="CreateMP"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="Game" Height="450" Width="400"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize" WindowStyle="None">
<Window.DataContext>
<local1:CreateMPViewModel/>
</Window.DataContext>
<Grid>
<Label Name ="CreateGame" Content="Create Game" Margin="109,46,118,322" FontSize="26" Foreground="#0074BC"/>
<Button Name ="btnCreateGame" Content="{Binding ButtonContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="35" Margin="118,267,118,0" VerticalAlignment="Top" Background="#0074BC" Foreground="White" Command="{Binding ButtonCommand_CreateGame}" IsEnabled="{Binding ButtonEnable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Name ="btnBack" Content="Back" Height="35" Margin="118,387,118,0" VerticalAlignment="Top" Background="#0074BC" Foreground="White" Command="{Binding ButtonCommand_Back}"/>
<Label Content="IP4-Adress:" HorizontalAlignment="Left" Height="30" Margin="92,138,0,0" VerticalAlignment="Top" Width="90" Foreground="#0074BC"/>
<Label Content="Password:" HorizontalAlignment="Left" Height="30" Margin="92,168,0,0" VerticalAlignment="Top" Width="90" Foreground="#0074BC"/>
<Label Content="{Binding IPAdresse}" HorizontalAlignment="Left" Height="30" Margin="187,138,0,0" VerticalAlignment="Top" Width="127" Foreground="#0074BC"/>
<Label Content="{Binding Password}" HorizontalAlignment="Left" Height="30" Margin="187,168,0,0" VerticalAlignment="Top" Width="127" Foreground="#0074BC"/>
</Grid>
</Window>
The CreateMPViewModel class must inherit from INotifyPropertyChanged.
public class CreateMPViewModel : Window, INotifyPropertyChanged
{
//........................................
//........................................
}
I have a UserControl called "UserControllerIo" and this is what it has:
public ObservableCollection<string> Messages { get; set; }
public UserControllerIo()
{
Messages = new ObservableCollection<string>();
InitializeComponent();
IoComponentViewModel.Instance = new IoComponentViewModel();
Label1.DataContext = IoComponentViewModel.Instance;
Messages.Add(Label1.Text);
}
I consume this in my xml like so:
<Grid>
<Label>
<TextBlock x:Name="Label1" TextWrapping="WrapWithOverflow"
Text="{Binding Path=XState, Mode=OneWay}">
</TextBlock>
</Label>
<ListView
x:Name="ListView"
ItemsSource="{Binding Messages}" />
</Grid>
I have a view model for this control:
class IoComponentViewModel : INotifyPropertyChanged
{
public static IoComponentViewModel Instance { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private string _xState;
public string XState
{
get { return _xState; }
set
{
_xState = value;
OnPropertyChanged($"XState");
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And I invoke to populate the list on another class like so:
case x:
IoComponentViewModel.Instance.XState = msg;
break;
My problem is, it is not showing in my Listview although I can see it in my label. Can you please show me how. Thank you.
I don't know how much I understood your task from the provided code, but look at this implementation variant.
IoComponentViewModel:
public class IoComponentViewModel : INotifyPropertyChanged
{
public static IoComponentViewModel Instance { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private string _xState;
public string XState
{
get { return _xState; }
set
{
if (_xState == value)
return;
XStates.Add(_xState = value);
OnPropertyChanged($"XState");
}
}
public ObservableCollection<string> XStates { get; } = new ObservableCollection<string>();
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Grid x:Name="PART_Grid">
<Grid.DataContext>
<local:IoComponentViewModel/>
</Grid.DataContext>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!--<Label>-->
<TextBlock x:Name="Label1" TextWrapping="WrapWithOverflow"
Text="{Binding XState, Mode=OneWay}">
</TextBlock>
<!--</Label>-->
<ListView Grid.Row="1"
x:Name="ListView"
ItemsSource="{Binding XStates}" />
</Grid>
Code Behind:
//public ObservableCollection<string> Messages { get; set; }
public UserControllerIo()
{
//Messages = new ObservableCollection<string>();
InitializeComponent();
// IoComponentViewModel.Instance = new IoComponentViewModel();
//Label1.DataContext = IoComponentViewModel.Instance;
IoComponentViewModel.Instance = (IoComponentViewModel)PART_Grid.DataContext;
//Messages.Add(Label1.Text);
}
I misread the question initially. There are two problems. Your list is not binding to the view model, so you need an element reference.
<UserControl x:Class="StackOverflow.UserControllerIo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:StackOverflow"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
x:Name="MyUserControl"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label>
<TextBlock Foreground="Black" x:Name="Label1" TextWrapping="WrapWithOverflow"
Text="{Binding Path=XState, Mode=OneWay}">
</TextBlock>
</Label>
<ListView Grid.Row="1"
x:Name="ListView"
ItemsSource="{Binding Messages, ElementName=MyUserControl}" >
</ListView>
</Grid>
Secondly, at the point where you add Label1.Text to your data binding is not ready. So you will need to wait for binding before you read the text, for example in load event like this:
public partial class UserControllerIo : UserControl
{
public ObservableCollection<string> Messages { get; set; }
public UserControllerIo()
{
Messages = new ObservableCollection<string>();
InitializeComponent();
IoComponentViewModel.Instance = new IoComponentViewModel();
Label1.DataContext = IoComponentViewModel.Instance;
IoComponentViewModel.Instance.XState = "Something";
Loaded += UserControllerIo_Loaded;
}
private void UserControllerIo_Loaded(object sender, RoutedEventArgs e)
{
Messages.Add(Label1.Text);
}
}
EDIT:
my first tests mislead me, by testing with an int property for adding values to the List on runtime.
ObservableCollection updates anyway!
The problem is how you declared the Messages Property. If you have a Property on a Control it needs to be a dependency Property to notify the UI.
replace
public ObservableCollection<string> Messages { get; set; }
with
public ObservableCollection<string> Messages
{
get { return (ObservableCollection<string>)GetValue(MessagesProperty); }
set { SetValue(MessagesProperty, value); }
}
public static readonly DependencyProperty MessagesProperty =
DependencyProperty.Register("Messages", typeof(ObservableCollection<string>), typeof(UserControllerIo), new PropertyMetadata(null));
and you should be fine.
OR
you could imlpement INotifyPropertyChanged on your UserControl class.
And don't forget to maintain #Clemens' comment about binding!!!
ItemsSource="{Binding Messages, RelativeSource={RelativeSource AncestorType=UserControl}}
I have a problem with the listviewItem, is that when you change the data if they do it but they are not saved in the interface when you click on another item
This problem happens when binding the textbox to the listviewItem
MainPage.xaml
<Grid RequestedTheme="Light">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="818*" />
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBox
x:Name="titulo"
Grid.Row="0"
FontSize="40"
PlaceholderText="Ingresa tu titulo"
KeyDown="Titulo_KeyDown"
/>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<ListView
x:Name="listNotas"
Width="450"
Background="DimGray"
SelectionChanged="ListNotas_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel>
<TextBlock Text="{Binding title, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<RichEditBox
x:Name="editor"
Width="760"
HorizontalAlignment="Stretch" />
</StackPanel>
<GridView
Name="stpanel"
Grid.Row="2"
Height="50">
<TextBlock Text="" Name="Tester"/>
</GridView>
MainPage.xaml.cs
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json" );
public ObservableCollection<Notes> Mynotes;
public MainPage()
{
this.InitializeComponent();
// Load data of Notas.json to Listview
LoadUpdate();
}
private void LoadUpdate()
{
using (StreamReader file = File.OpenText(editpath))
{
var json = file.ReadToEnd();
baseNotes mainnotes = JsonConvert.DeserializeObject<baseNotes>(json);
Mynotes = new ObservableCollection<Notes>();
foreach (var item in mainnotes.notes)
{
Mynotes.Add(new Notes { title = item.title });
}
listNotas.ItemsSource = null;
listNotas.ItemsSource = Mynotes;
listNotas.SelectedIndex = 0;
}
}
private void ListNotas_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string json = File.ReadAllText(editpath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
titulo.Text = jsonObj["notes"][listNotas.SelectedIndex]["title"];
}
private void Titulo_KeyDown(object sender, KeyRoutedEventArgs e)
{
#region
string json = File.ReadAllText(editpath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
int indice = listNotas.SelectedIndex;
jsonObj["notes"][indice]["title"] = titulo.Text;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj);
File.WriteAllText(editpath, output);
// Show json file text in RicheditBox
editor.TextDocument.SetText(Windows.UI.Text.TextSetOptions.None, output);
//Problem
Binding myBinding = new Binding();
myBinding.Source = Mynotes[indice];
myBinding.Path = new PropertyPath("title");
myBinding.Mode = BindingMode.TwoWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(titulo, TextBox.TextProperty, myBinding);
#endregion
}
Model: Notes.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
namespace Realtimejsonedit
{
public class Notes : INotifyPropertyChanged
{
public int created { get; set; }
//public string title { get; set; }
private string Title;
public string title
{
get { return Title; }
set {
Title = value;
NotifyPropertyChanged("title");
}
}
public string text { get; set; }
public int id { get; set; }
public int updated { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class baseNotes
{
public List<Notes> notes { get; set; }
}
}
as I say the problem as I am doing the binding but when executing ListNotas.SelectionChanged the values that were saved in the json file are changed, but they do not remain in the listviewitem, although the binding is in the Keydown event and not in ListNotas. SelectionChanged.
the problem:
https://i.imgur.com/IGcd8iz.gif
What I want to achieve:
https://i.imgur.com/KnkbQw9.gif
UWP - How to save ListViewItem state if the data source has changed?
The problem is that you set bind repeatedly in Titulo_KeyDown event. For your requirement, you could bind ListView SelectItem once. For more please refer the following steps:
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json");
public ObservableCollection<Notes> Mynotes { get; set; }
public ViewModel()
{
LoadUpdate();
SetSelectIndex(0);
}
private void SetSelectIndex(int index)
{
SelectItem = Mynotes[index];
}
private void LoadUpdate()
{
using (StreamReader file = File.OpenText(editpath))
{
var json = file.ReadToEnd();
baseNotes mainnotes = JsonConvert.DeserializeObject<baseNotes>(json);
Mynotes = new ObservableCollection<Notes>();
foreach (var item in mainnotes.notes)
{
Mynotes.Add(new Notes { title = item.title });
}
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Notes _selectItem;
public event PropertyChangedEventHandler PropertyChanged;
public Notes SelectItem
{
get
{
return _selectItem;
}
set
{
_selectItem = value;
OnPropertyChanged();
}
}
}
Xaml
<Page.DataContext>
<local:ViewModel />
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="818*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBox
x:Name="titulo"
Grid.Row="0"
FontSize="40"
PlaceholderText="Ingresa tu titulo"
Text="{Binding SelectItem.title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="Titulo_TextChanged"
/>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<ListView
x:Name="listNotas"
Width="450"
Background="DimGray"
ItemsSource="{Binding Mynotes}"
SelectedItem="{Binding SelectItem, Mode=TwoWay}"
>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<RichEditBox
x:Name="editor"
Width="760"
HorizontalAlignment="Stretch"
/>
</StackPanel>
<GridView
Name="stpanel"
Grid.Row="2"
Height="50"
>
<TextBlock Name="Tester" Text="" />
</GridView>
</Grid>
Code behind (write the data to json)
public sealed partial class MainPage : Page
{
private dynamic jsonObj;
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json");
public ObservableCollection<Notes> Mynotes;
public MainPage()
{
this.InitializeComponent();
string json = File.ReadAllText(editpath);
jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
}
private void Titulo_TextChanged(object sender, TextChangedEventArgs e)
{
#region
int indice = listNotas.SelectedIndex;
jsonObj["notes"][indice]["title"] = titulo.Text;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj);
editor.TextDocument.SetText(Windows.UI.Text.TextSetOptions.None, output);
File.WriteAllText(editpath, output);
#endregion
}
}
This is sample project.
So I've been trying to figure this out for 3 days and I just can't seem to find a solution.
This is what I am trying to achieve.
I have a simple WPF project with a RichTextBox in it.
What my application is doing is that it acts like a CMD.
What I want to do now is that I want to change the message it saved when I press enter, I want the previous message to change color.
Here is a GIF showing what it looks like
https://i.imgur.com/srszUKG.gifv
I tried binding the Foreground of the TextBox inside the DataTemplate but that just made it to where the text wouldnt even show up.
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" Name="SavedBlocks" FontFamily="Consolas"/>
</DataTemplate>
So what are my options here, I essentially want to change the color of the text depending on how long the message is.
XAML
<Window x:Class="WpfApp1Eh.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1Eh"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ScrollViewer Name="Scroller" Margin="0" Background="Black">
<StackPanel>
<ItemsControl ItemsSource="{Binding ConsoleOutput, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" Name="SavedBlocks" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox Text="{Binding ConsoleInput, Mode=TwoWay}" Background="Black" Foreground="White" FontFamily="Consolas" Name="InputBlock" BorderBrush="{x:Null}" SelectionBrush="{x:Null}" />
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
main.cs
public partial class MainWindow : Window
{
ConsoleContent dc = new ConsoleContent();
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded1;
DataContext = dc;
}
private void MainWindow_Loaded1(object sender, RoutedEventArgs e)
{
InputBlock.KeyDown += InputBlock_KeyDown;
InputBlock.Focus();
}
void InputBlock_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
dc.ConsoleInput = InputBlock.Text;
dc.RunCommand();
InputBlock.Focus();
Scroller.ScrollToBottom();
}
}
}
ConsoleContent.cs
public class ConsoleContent : INotifyPropertyChanged
{
string consoleInput = string.Empty;
ObservableCollection<string> consoleOutput = new ObservableCollection<string>() { "Console Emulation Sample..." };
public string ConsoleInput
{
get
{
return consoleInput;
}
set
{
consoleInput = value;
OnPropertyChanged("ConsoleInput");
}
}
public ObservableCollection<string> ConsoleOutput
{
get
{
return consoleOutput;
}
set
{
consoleOutput = value;
OnPropertyChanged("ConsoleOutput");
}
}
public void RunCommand()
{
ConsoleOutput.Add(ConsoleInput);
//myBrush = Brushes.Orange;
// do your stuff here.
ConsoleInput = String.Empty;
}
private System.Windows.Media.Brush _foregroundColor = System.Windows.Media.Brushes.DarkSeaGreen;
public System.Windows.Media.Brush ForegroundColor
{
get { return _foregroundColor; }
set
{
_foregroundColor = value;
OnPropertyChanged("ForegroundColor");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Change the type of consoleOutput from ObservableCollection<string> to ObservableCollection<YourType> where YourType is a class that represents a line of input with a text string and a Foreground Brush:
public class YourType : INotifyPropertyChanged
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; OnPropertyChanged("Text"); }
}
private Brush _foreground;
public Brush Foreground
{
get { return _foreground; }
set { _foreground = value; OnPropertyChanged("Foreground"); }
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Bind to the properties of this class in your XAML:
<ScrollViewer Name="Scroller" Margin="0" Background="Black">
<StackPanel>
<ItemsControl ItemsSource="{Binding ConsoleOutput, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"
Foreground="{Binding Foreground}"
Name="SavedBlocks" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox Text="{Binding ConsoleInput, Mode=TwoWay}" Background="Black" Foreground="White" FontFamily="Consolas" Name="InputBlock" BorderBrush="{x:Null}" SelectionBrush="{x:Null}" />
</StackPanel>
</ScrollViewer>
You can then set the Foreground property of each individual item in the source collection:
public void RunCommand()
{
ConsoleOutput.Add(new YourType { Text = ConsoleInput, Foreground = Brushes.Orange } );
ConsoleInput = String.Empty;
}
I have a class News
public class News : ObservableCollection<New>
{
public News()
: base()
{
}
}
A class New
public class New : INotifyPropertyChanged
{
public PhotoAttachments Photo
{
get
{
return photoAttachments;
}
set
{
photoAttachments = value;
OnPropertyChanged("Photo");
}
}
// some fields such as date, text, id, sourceName etc
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{//realisation of method}
public PhotoAttachments photoAttachments = new PhotoAttachments(); // it is a collection, such as News, but it contains objects of class PhotoAttachment, which have property with string url to photo
}
after
InitializeComponent(); i write this.listBox.ItemsSource = NewsList;
so a have a listbox with objects of class New.
In these listbox I created another one listbox, and tried to fill it by PhotoAttachments collection. And here I have a problem, listbox with photos don't show photos(but they exists). Here is XAML:
// I can select different <local:NewsTemplateSelector.Photos>
//style of listbox <DataTemplate>
//using NewsTemplateSelector <Border BorderBrush="Red" BorderThickness="2" Width="400" Height="300" Margin="10">
<StackPanel Orientation="Horizontal" Width="400" Height="300">
<Image Source="{Binding SourceImage}" Height="75" Width="75" Margin="0,-225,0,0" />
<Canvas Width="400">
<TextBlock Text="{Binding SourceName}" Foreground="Black" FontSize="25" TextWrapping="Wrap" Height="65" Width="326" d:LayoutOverrides="VerticalAlignment, Height" />
<ListBox Name="photoListbox" ItemsSource="{Binding Photo}" Height="229" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="326" Canvas.Top="69">
<Image Source="{Binding Big}" Height="200" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="400" />
</ListBox>
</Canvas>
</StackPanel>
</Border>
</DataTemplate>
</local:NewsTemplateSelector.Photos>
PhotoAttachment class:
public class PhotoAttachment : INotifyPropertyChanged
{
private string ownerId;
public string OwnerId { get { return ownerId; } set { ownerId = value; OnPropertyChanged("OwnerId"); } }
private string small;
public string Small { get { return small; } set { small = value; OnPropertyChanged("Small"); } }
private string big;
public string Big { get { return big; } set { big = value; OnPropertyChanged("Big"); } }
public PhotoAttachment(string ownId, string small, string big)
{
ownerId = ownId;
this.small = small;
this.big = big;
}
public PhotoAttachment() { }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Just realized that your XAML for photoListView is missing an ItemTemplate
Something along these lines should do the trick:
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Big}" Height="200" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="400" />
</DataTemplate>
</ListBox.ItemTemplate>