我有一个图表,我想要更改一些ViewModel属性,以便整个图形会相应更改。
我想在这里更改的唯一属性是" year",我尝试实现INotifyPropertyChanged,因此绑定会导致图表自动更改,但它无法正常工作。
这是模型:
public class Model
{
public double rate { get; set; }
public string date { get; set; }
}
这是ViewModel:
public class ViewModel :INotifyPropertyChanged
{
private string _year;
public string Year { get { return _year; } set { _year = value;UpdateData(); OnPropertyChanged("Year"); } }
public ViewModel()
{
_year = "2017";
UpdateData();
}
public void UpdateData()
{
int i,j;//Indexs that holds actuall api retrived values
string cR, urlContents;// cR- current rate in string format, urlContents - the whole Api retrived data
string c;//For api syntx, add 0 or not, depends on the current date syntax
this.CurrenciesHis = new ObservableCollection<Model>();//Model objects collection
HttpClient client = new HttpClient();
for (int l = 1; l < 13; l++)
{
if (l < 10)
c = "0";
else
c = "";
urlContents = client.GetStringAsync("http://data.fixer.io/api/"+(_year)+"-"+ c + l + "-01?access_key=&base=USD&symbols=EUR&format=1").Result;
i = urlContents.IndexOf("EUR");//Finds the desired value from api recived data
j = urlContents.IndexOf("}");
cR = urlContents.Substring(i + 5, (j - 2) - (i + 5));
CurrenciesHis.Add(new Model() { rate = Convert.ToDouble(cR), date = "01/" + l.ToString() });
}
}
public ObservableCollection<Model> CurrenciesHis { get; set; }
#region "INotifyPropertyChanged members"
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion
这是基于第三方控件的视图(我删除了很多XAML并使用粗体字母来标记实际绑定所在的位置):
<layout:SampleLayoutWindow x:Class="AreaChart.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ResizeMode="CanResizeWithGrip"
xmlns:chart="clr-namespace:Syncfusion.UI.Xaml.Charts;assembly=Syncfusion.SfChart.WPF"
xmlns:local="clr-namespace:PL"
xmlns:layout="clr-namespace:Syncfusion.Windows.SampleLayout;assembly=Syncfusion.Chart.Wpf.SampleLayout"
UserOptionsVisibility="Collapsed"
WindowStartupLocation="CenterScreen" Height="643.287" Width="1250.5"
Title="2017">
<Grid>
<Grid.Resources>
...........................................
<chart:AreaSeries x:Name="AreaSeries" EnableAnimation="True"
**XBindingPath="date"
Label="Favourite"
YBindingPath="rate"
ItemsSource="{Binding CurrenciesHis}"**
ShowTooltip="True" >
<chart:AreaSeries.AdornmentsInfo>
<chart:ChartAdornmentInfo AdornmentsPosition="Bottom"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ShowLabel="True">
<chart:ChartAdornmentInfo.LabelTemplate>
<DataTemplate>
....................................
<TextBox HorizontalAlignment="Left" Height="30" Margin="28,231,0,0" TextWrapping="Wrap" Name="Text1" VerticalAlignment="Top" Width="76" Text="{Binding Year, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
这是代码行为和我想要更改的文本框事件,它帮助viewmodel的年属性:
public partial class MainWindow : SampleLayoutWindow
{
PL.ViewModel newInstance;
public MainWindow()
{
InitializeComponent();
newInstance = new PL.ViewModel();
this.DataContext = newInstance;
}
}
据我所知,从这一点来看,WPF的机制将使用绑定和&#34;通知&#34;来更改图表上的值。 INotifyPropertyChanged,但它对我不起作用..
答案 0 :(得分:1)
year
应该是私有字段,但它是公开的。您正在设置字段的值,该字段自然不会执行属性的setter中的任何代码。
将year
和所有支持字段设为私有,并使用前导下划线重命名所有私有字段(例如,year
应重命名为_year
)以防止发生意外像这样。
并在viewmodel代码中设置一个策略,始终设置属性,而不是字段,当然除了该字段的实际属性设置器之外。
此外,使用绑定从UI设置viewmodel属性。不要在代码隐藏中这样做。摆脱那个textchanged处理程序。
<TextBox
HorizontalAlignment="Left"
Height="30"
Margin="28,231,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="76"
Text="{Binding Year, UpdateSourceTrigger=PropertyChanged}"
/>
最后,您似乎打算对Year
的更改对CurrenciesHis
的内容产生一些影响,但在您的代码中没有相应的机制,也没有解释您想要的内容已经发生或者你期望它如何发生。
这是您的viewmodel的更新版本。
public class ViewModel
{
public ViewModel()
{
// DO NOT, DO NOT EVER, DO NOT, SERIOUSLY, EVER, EVER, EVER UPDATE A
// PROPERTY'S BACKING FIELD OUTSIDE THE PROPERTY'S SETTER.
Year = DateTime.Now.Year - 1;
UpdateCurrencies();
}
protected void UpdateCurrencies()
{
// Indexs that holds actuall api retrived values
int i, j;
// cR- current rate in string format, urlContents - the whole Api retrived data
string cR, urlContents;
// For api syntx, add 0 or not, depends on the current date syntax
string c;
CurrenciesHis = new ObservableCollection<Model>();//Model objects collection
HttpClient client = new HttpClient();
for (int l = 1; l < 13; l++)
{
if (l < 10)
c = "0";
else
c = "";
// Use the public property Year, not the field _year
var url = "http://data.fixer.io/api/" + Year + "-" + c + l + "-01?access_key=&base=USD&symbols=EUR&format=1";
urlContents = client.GetStringAsync(url).Result;
i = urlContents.IndexOf("EUR");//Finds the desired value from api recived data
j = urlContents.IndexOf("}");
cR = urlContents.Substring(i + 5, (j - 2) - (i + 5));
CurrenciesHis.Add(new Model() { rate = Convert.ToDouble(cR), date = "01/" + l.ToString() });
}
OnPropertyChanged(nameof(CurrenciesHis));
}
// Year is an integer, so make it an integer. The binding will work fine,
// and it'll prevent the user from typing "lol".
private int _year;
public int Year
{
get { return _year; }
set
{
if (_year != value)
{
_year = value;
OnPropertyChanged(nameof(Year));
UpdateCurrencies();
}
}
}
public ObservableCollection<Model> CurrenciesHis { get; private set; }
// -----------------------------------------------------
// YearsList property for ComboBox
// 30 years, starting 30 years ago.
// You could make this IEnumerable<int> or ReadOnlyCollection<int> if you
// want something other than the ComboBox to use it. The ComboBox doesn't care.
// Year MUST be an int for the binding to SelectedItem (see below) to work,
// not a string.
public System.Collections.IEnumerable YearsList
=> Enumerable.Range(DateTime.Now.Year - 30, 30).ToList().AsReadOnly();
}
XAML for YearsList组合框(我更喜欢文本框btw):
<ComboBox
ItemsSource="{Binding YearsList}"
SelectedItem="{Binding Year}"
/>
答案 1 :(得分:0)
你的CurrenciesHis属性并没有实现INPC,所以WPF没有意识到你改变了它(UpdateData()有&#34; this.CurrenciesHis = new ObservableCollection();&#34;)
您当前的财产是:
public ObservableCollection<Model> CurrenciesHis { get; set; }
应该是这样的:
private ObservableCollection<Model> _CurrenciesHis;
public ObservableCollection<Model> CurrenciesHis { get { return _CurrenciesHis; } set { if (_CurrenciesHis != value) { _CurrenciesHis = value; OnPropertyChanged("CurrenciesHis"); } } }