|
 yes you can.
Below find my the base ModelBase class that I use, that uses data anntoation to validate its property when OnPropertyChanged() is fired, with caveat it uses my own attributes, and might be missing some stuff.. but it should give you an idea!
public abstract class ValidationAttribute : Attribute
{
public abstract void Validate(ModelBase model, FastMember member, object value);
public string ErrorMessage { get; set; }
}
public class ModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
{
#region INotifyPropertyChanged
public virtual void OnPropertyChanged([CallerMemberName]string name = "")
{
if (ValidatePropertyOnNotifyChanged)
{
if (string.IsNullOrEmpty(name))
{
Validate();
}
else
{
Validate(name);
}
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region ValidatePropertyOnNotifyChanged Validate()
public bool ValidatePropertyOnNotifyChanged
{
get { return mValidatePropertyOnNotifyChanged; }
set
{
if (value == mValidatePropertyOnNotifyChanged)
return;
mValidatePropertyOnNotifyChanged = value;
if (value)
Validate();
}
}
bool mValidatePropertyOnNotifyChanged;
public bool Validate()
{
ClearErrors();
foreach (var p in GetFastType().GetRuntimeMembers())
Validate(p);
return !HasErrors;
}
#endregion
#region Errors (databinding error friend)
public ErrorIndex Errors
{
get
{
if (eInfos == null)
eInfos = new ErrorIndex(this);
return eInfos;
}
}
ErrorIndex eInfos;
public class ErrorIndex : INotifyPropertyChanged
{
ModelBase model;
internal ErrorIndex(ModelBase model)
{
this.model = model;
}
public ErrorData this[string property]
{
get
{
var errs = model.GetErrors(property).Cast<object>();
return new ErrorData
{
HasError = errs.Any(),
Error = errs.FirstOrDefault(),
Errors = errs,
};
}
}
internal void Updated(string pName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pName)); }
public event PropertyChangedEventHandler PropertyChanged;
}
public class ErrorData
{
public bool HasError { get; set; }
public object Error { get; set; }
public System.Collections.IEnumerable Errors { get; set; }
}
#endregion
#region INotifyDataErrorInfo
List<Tuple<string, object>> errors
{
get
{
if (_errors == null)
_errors = new List<Tuple<string, object>>();
return _errors;
}
}
List<Tuple<string, object>> _errors;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
void RaiseErrorChanged(string pName)
{
pName = '[' + pName + ']';
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(pName));
OnPropertyChanged(nameof(HasErrors));
eInfos?.Updated(pName);
}
public System.Collections.IEnumerable GetErrors(string propertyName)
{
return errors.Where(x => x.Item1 == propertyName).Select(x => x.Item2);
}
public bool HasErrors { get { return errors.Count > 0; } }
public void UpdateErrors(string propertyName, params object[] errors) { UpdateErrors(propertyName, (IEnumerable<object>)errors); }
public void UpdateErrors(string propertyName, IEnumerable<object> errors)
{
bool had = false;
for (int i = this.errors.Count - 1; i >= 0; i--)
{
if (this.errors[i].Item1 == propertyName)
{
had = true;
this.errors.RemoveAt(i);
}
}
bool will = false;
if (errors != null)
{
errors.ForEach(x =>
{
will = true;
this.errors.Add(Tuple.Create(propertyName, x));
});
}
if (had || will)
{
RaiseErrorChanged(propertyName);
}
}
public void AddErrors(string propertyName, params object[] errors) { AddErrors(propertyName, (IEnumerable<object>)errors); }
public void AddErrors(string propertyName, IEnumerable<object> errors)
{
if (errors == null || errors.Count() == 0)
return;
errors.ForEach(x => this.errors.Add(Tuple.Create(propertyName, x)));
RaiseErrorChanged(propertyName);
}
public void ClearErrors(string propertyName)
{
int count = 0;
for (int i = errors.Count - 1; i >= 0; i--)
{
if (errors[i].Item1 == propertyName)
{
errors.RemoveAt(i);
count++;
}
}
if (count > 0)
RaiseErrorChanged(propertyName);
}
public void ClearErrors()
{
var names = errors.Select(x => x.Item1).Distinct().ToList();
errors.Clear();
names.ForEach(x => RaiseErrorChanged(x));
}
public bool Validate([CallerMemberName]string propertyName = null)
{
if (propertyName == null)
return Validate();
var m = GetFastType().GetRuntimeMembers().First(x => x.Name == propertyName);
ClearErrors(propertyName);
Validate(m);
return !GetErrors(propertyName).Cast<object>().Any();
}
FastType GetFastType() { return meType ?? (meType = FastType.GetType((GetType()))); }
FastType meType;
protected virtual void Validate(FastMember pi)
{
if (pi == null)
return;
var value = pi.GetValue(this);
var attrs = pi.GetAttributes().OfType<ValidationAttribute>();
foreach (var attr in attrs)
attr.Validate(this, pi, value);
}
#endregion
#region extra utilities: GetLazyCommand() AddWatch()
public DelegateCommand GetLazyCommand(Action onAction, [CallerMemberName]string name = null)
{
if (mCommands == null)
mCommands = new Dictionary<string, DelegateCommand>();
if (!mCommands.TryGetValue(name, out var result))
mCommands[name] = result = new DelegateCommand(onAction);
return result;
}
public DelegateCommand GetLazyCommand(Action<object> onAction, [CallerMemberName]string name = null)
{
if (mCommands == null)
mCommands = new Dictionary<string, DelegateCommand>();
if (!mCommands.TryGetValue(name, out var result))
mCommands[name] = result = new DelegateCommand(onAction);
return result;
}
public DelegateCommand GetLazyCommandAsync(Func<Task> onAction, [CallerMemberName]string name = null)
{
if (mCommands == null)
mCommands = new Dictionary<string, DelegateCommand>();
if (!mCommands.TryGetValue(name, out var result))
{
var cmd = new DelegateCommand();
cmd.Action = async (o) =>
{
try
{
cmd.IsEnabled = false;
await onAction();
}
finally { cmd.IsEnabled = true; }
};
mCommands[name] = result = cmd;
}
return result;
}
Dictionary<string, DelegateCommand> mCommands;
public void AddWatch<TP>(Expression<Func<TP>> e, Action<TP> onChange)
{
if (watchers == null)
watchers = new List<PropertyPath>();
var w = PropertyPath.Create(e);
watchers.Add(w);
w.PropertyChanged += delegate { onChange(w.Value); };
}
List<PropertyPath> watchers;
#endregion
}
|
|
|
|
|
Here's my two extension methods for checking if a property or an IDataErrorInfo object has any errors:
public static bool HasErrors( this IDataErrorInfo dataObject ) {
Type dataObjectType = dataObject.GetType();
PropertyInfo[] pInfo = dataObjectType.GetProperties( BindingFlags.Public | BindingFlags.Instance );
foreach ( PropertyInfo p in pInfo ) {
if ( p.CanWrite ) {
if ( string.IsNullOrEmpty( dataObject[ p.Name ] ) == false ) {
return true;
}
}
}
return false;
}
public static bool HasErrors( this IDataErrorInfo dataObject, string name ) {
if ( string.IsNullOrEmpty( dataObject[ name ] ) == false ) {
return true;
}
return false;
}
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
<DataGrid Grid.Column="1" x:Name="GrdInsCardsLookup" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxWidth="600" Margin="3,6,3,-1" Background="{x:Null}">
<DataGrid.BorderBrush>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FFE22020" Offset="1"/>
</LinearGradientBrush>
</DataGrid.BorderBrush>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Width="130" Binding="{Binding NationalId}"/>
<DataGridTextColumn Header="Name" Width="140" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Phone" Width="120" Binding="{Binding Phone}"/>
<DataGridTextColumn Header="City" Width="100" Binding="{Binding City}"/>
</DataGrid.Columns>
</DataGrid>
public class Instructer
{
public int Id { set; get; }
public string NationalId { set; get; }
public string Name { set; get; }
public string Fname { set; get; }
public string Lname { set; get; }
public string FatherName { set; get; }
public int Cityid { set; get; }
public string City { set; get; }
public string Address { set; get; }
public string Email { set; get; }
public string Phone { set; get; }
public string Mobile { set; get; }
public DateTime Birthday { set; get; }
public string Picid { set; get; }
public string Folder { set; get; }
}
public DataSet AllInsDataSet = new DataSet();
public List<Instructer> InstructersByName = new List<Instructer>();
private void FillInsList()
{
AllInsDataSet.Clear();
var con1 = new SqlConnection(Connectionstring);
SqlCommand cmd1;
cmd1 =
new SqlCommand(
"SELECT S1.[ins_id],S1.[ins_national_id],S1.[fname] as FName,S1.[father_name] as 'FatherName',S1.[lname] as 'LName',S1.[city_id],C1.[city],S1.[address],S1.[email],S1.[phone],S1.[mobile],S1.[birthday],S1.[pic_id],S1.[folder_id],S1.[other] FROM [DB_SchoolManager].[dbo].[tbl_ins_cards] S1 Inner Join [DB_SchoolManager].[dbo].[tbl_cities] C1 on S1.city_id=C1.city_id",
con1);
SqlDataAdapter da1;
da1 = new SqlDataAdapter(cmd1);
da1.Fill(AllInsDataSet);
}
private void FillLookupInsGrdByName(string name)
{
InstructersByName.Clear();
GrdStuCardsLookup.ItemsSource = InstructersByName;
string upname = name.ToUpper();
for (int i = 0; i < AllInsDataSet.Tables[0].Rows.Count; i++)
{
if (((AllInsDataSet.Tables[0].Rows[i][2] + " " + AllInsDataSet.Tables[0].Rows[i][3] + " " + AllInsDataSet.Tables[0].Rows[i][4]).ToUpper()).Contains(upname))
{
InstructersByName.Add(new Instructer()
{
Id = int.Parse(AllInsDataSet.Tables[0].Rows[i][0].ToString()),
NationalId = AllInsDataSet.Tables[0].Rows[i][1].ToString(),
Name = AllInsDataSet.Tables[0].Rows[i][2] + " " + AllInsDataSet.Tables[0].Rows[i][3] + " " + AllInsDataSet.Tables[0].Rows[i][4],
City = AllInsDataSet.Tables[0].Rows[i][6].ToString(),
Phone = AllInsDataSet.Tables[0].Rows[i][9].ToString(),
Picid = AllInsDataSet.Tables[0].Rows[i][12].ToString(),
Folder = AllInsDataSet.Tables[0].Rows[i][13].ToString()
});
}
}
GrdInsCardsLookup.ItemsSource = InstructersByName;
}
private void TxtSrchInsName_TextChanged(object sender, TextChangedEventArgs e)
{
if (!String.IsNullOrEmpty(TxtSrchInsName.Text))
{
SrchName = TxtSrchInsName.Text;
FillLookupInsGrdByName(SrchName);
}
}
|
|
|
|
|
so.. what is the exception you are getting?
From the look of it, it looks like you are binding to a list which has been updated after being given to the UI.
Hence the UI display the original list, that do not match the current list.
To solve that problem use a class implementing INotifyCollectionChanged . I recommend ObservableCollection<T> instead of List<T>
|
|
|
|
|
First thank you very much
i dont no the deference between List<t> and ObservableCollection<t> but it works
|
|
|
|
|
ObservableCollection<T> implements INotifyCollectionChanged
This is the interface that helps ListItem s synchronise with lists.
Glad it fixed your problem!
In visual studio, press "F12" when the cursor is on an unknown class and watch and learn!
"F1" will also be interesting if the class is part of Microsoft API...
|
|
|
|
|
You've posted the same question twice, and still managed to not provide the details of the exception.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
XML Code
<DataGrid Grid.Column="1" x:Name="GrdInsCardsLookup" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxWidth="600" Margin="3,6,3,-1" Background="{x:Null}">
<DataGrid.BorderBrush>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FFE22020" Offset="1"/>
</LinearGradientBrush>
</DataGrid.BorderBrush>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Width="130" Binding="{Binding NationalId}"/>
<DataGridTextColumn Header="Name" Width="140" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Phone" Width="120" Binding="{Binding Phone}"/>
<DataGridTextColumn Header="City" Width="100" Binding="{Binding City}"/>
</DataGrid.Columns>
</DataGrid>
C# Code
public DataSet AllInsDataSet = new DataSet();
public List<Instructer> InstructersByName = new List<Instructer>();
private void FillInsList()
{
AllInsDataSet.Clear();
var con1 = new SqlConnection(Connectionstring);
SqlCommand cmd1;
cmd1 =
new SqlCommand(
"SELECT S1.[ins_id],S1.[ins_national_id],S1.[fname] as FName,S1.[father_name] as 'FatherName',S1.[lname] as 'LName',S1.[city_id],C1.[city],S1.[address],S1.[email],S1.[phone],S1.[mobile],S1.[birthday],S1.[pic_id],S1.[folder_id],S1.[other] FROM [DB_SchoolManager].[dbo].[tbl_ins_cards] S1 Inner Join [DB_SchoolManager].[dbo].[tbl_cities] C1 on S1.city_id=C1.city_id",
con1);
SqlDataAdapter da1;
da1 = new SqlDataAdapter(cmd1);
da1.Fill(AllInsDataSet);
}
private void FillLookupInsGrdByName(string name)
{
InstructersByName.Clear();
GrdStuCardsLookup.ItemsSource = InstructersByName;
string upname = name.ToUpper();
for (int i = 0; i < AllInsDataSet.Tables[0].Rows.Count; i++)
{
if (((AllInsDataSet.Tables[0].Rows[i][2] + " " + AllInsDataSet.Tables[0].Rows[i][3] + " " + AllInsDataSet.Tables[0].Rows[i][4]).ToUpper()).Contains(upname))
{
InstructersByName.Add(new Instructer()
{
Id = int.Parse(AllInsDataSet.Tables[0].Rows[i][0].ToString()),
NationalId = AllInsDataSet.Tables[0].Rows[i][1].ToString(),
Name = AllInsDataSet.Tables[0].Rows[i][2] + " " + AllInsDataSet.Tables[0].Rows[i][3] + " " + AllInsDataSet.Tables[0].Rows[i][4],
City = AllInsDataSet.Tables[0].Rows[i][6].ToString(),
Phone = AllInsDataSet.Tables[0].Rows[i][9].ToString(),
Picid = AllInsDataSet.Tables[0].Rows[i][12].ToString(),
Folder = AllInsDataSet.Tables[0].Rows[i][13].ToString()
});
}
}
GrdInsCardsLookup.ItemsSource = InstructersByName;
}
public class Instructer
{
public int Id { set; get; }
public string NationalId { set; get; }
public string Name { set; get; }
public string Fname { set; get; }
public string Lname { set; get; }
public string FatherName { set; get; }
public int Cityid { set; get; }
public string City { set; get; }
public string Address { set; get; }
public string Email { set; get; }
public string Phone { set; get; }
public string Mobile { set; get; }
public DateTime Birthday { set; get; }
public string Picid { set; get; }
public string Folder { set; get; }
}
modified 16-Dec-18 7:53am.
|
|
|
|
|
My form has a ScrollViewer (horizontally oriented), with elements consisting of a set of UserControls. Basically layed out like this:
<ScrollViewer Name="TrackScroller" . . .
<Grid x:Name="TrackGrid" . . .
<Grid.ColumnDefinitions>
...
<local:TrackModule x: . . .
What I'd like to know, before I delve into it and possibly make myself crazy, is it possible to reorder the elements (the TrackModules) by dragging them to new positions in the grid? And, if so, will everything adjust itself afterward automatically?
Searching, I find tons of stuff about reordering DataGrid columns, but this isn't a DataGrid, and reordering rows of plain vanilla Grids, but nothing about columns.
Is what I'm thinking of doing even possible? (This is just a feature I'm idly thinking about, nothing even close to vital, so ROI is a factor, too.)
|
|
|
|
|
Normal panels are not quite friendly to drag operation (except perhaps canvas panel)
But I can imagine that one could create an adorner on mouse down, drag the adorner on mouse drag, update items Grid.Column on mouse up of the adorner
if you don't know about adorners:
Adorners Overview | Microsoft Docs
|
|
|
|
|
Thanks. I'll look into it.
|
|
|
|
|
Can someone show me how to create a Border with a background that pulsates from one color to another. I'm looking for a two-color fade in/out. The colors must be bound to property.
The colors will be set in response to a Status DP being changed.
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Find below a simple pulsing brush class I am using
public class PulsedBrush
{
public PulsedBrush()
{
Brush = new SolidColorBrush();
}
public PulsedBrush(Func<double, Color> get)
: this()
{
GetColor = get;
Update();
}
public Func<double, Color> GetColor { get; set; }
public SolidColorBrush Brush { get; private set; }
public static double DaySeconds
{
get
{
return DateTime.Now.TimeOfDay.Ticks / (double)TimeSpan.TicksPerSecond;
}
}
public void Update()
{
if (GetColor != null)
{
Brush.Color = GetColor(DaySeconds);
}
}
}
And simply call the Update() method on the CompositionTarget.Rendering event
|
|
|
|
|
I have a list of booleans, and I want to bind checkboxes to this list.\
Code Behind
private List<bool> _OnOffValues;
public List<bool> OnOffValues
{
get { return _OnOffValues; }
set
{
if (_OnOffValues != value)
{
_OnOffValues = value;
RaisePropertyChanged("OnOffValues");
}
}
}
public MyControl()
{
InitializeComponent();
this.DataContext = this;
OnOffValues = new List<bool>();
for (int x = 0; x < 24; x++)
{
OnOffValues.Add(true);
}
}
XAML
<CheckBox Grid.Row="0"
Grid.Column="0"
IsChecked="{Binding OnOffValues[0], ElementName=control}"/>
The output window shows
System.Windows.Data Error: 17 : Cannot get 'Item[]' value (type 'Boolean') from 'OnOffValues' (type 'List`1'). BindingExpression:Path=OnOffValues[0]; DataItem='MyControl' (Name='control'); target element is 'CheckBox' (Name=''); target property is 'IsChecked' (type 'Nullable`1') ArgumentOutOfRangeException:'System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: index'
The code in the contructor runs and the list is populated, but the output message appears BEFORE that.
What am I doing wrong here??
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Your binding assumes that OnOffValues is a dependency property of the control.
"As beings of finite lifespan, our contributions to the sum of human knowledge is one of the greatest endeavors we can undertake and one of the defining characteristics of humanity itself"
|
|
|
|
|
There's nothing there that says it's expecting a DP.
This space for rent
|
|
|
|
|
Look at his binding, IsChecked="{Binding OnOffValues[0], ElementName=control}" and the property, OnOffValues , is in the code behind of his user control. If he changes the property to a dependency property he won't get the error.
"As beings of finite lifespan, our contributions to the sum of human knowledge is one of the greatest endeavors we can undertake and one of the defining characteristics of humanity itself"
|
|
|
|
|
As explained below, it looks like he's using INPC so this is completely irrelevant.
This space for rent
|
|
|
|
|
Something like this,
Code behind
public partial class SomeControl : UserControl
{
public SomeControl()
{
InitializeComponent();
for (int i = 0; i < 20; i++)
{
OnOffValues.Add(true);
}
}
public List<bool> OnOffValues
{
get { return (List<bool>)GetValue(OnOffValuesProperty); }
set { SetValue(OnOffValuesProperty, value); }
}
public static readonly DependencyProperty OnOffValuesProperty =
DependencyProperty.Register("OnOffValues", typeof(List<bool>),
typeof(SomeControl), new PropertyMetadata(new List<bool>()));
}
XAML
<CheckBox IsChecked="{Binding Path=OnOffValues[0], RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"/>
"As beings of finite lifespan, our contributions to the sum of human knowledge is one of the greatest endeavors we can undertake and one of the defining characteristics of humanity itself"
|
|
|
|
|
He's set the UserControl as its own DataContext. He doesn't need to create a DP if he's set the UC to implement INotifyPropertyChanged (which the property change notification suggests).
This space for rent
|
|
|
|
|
Quite true, I had taken note of that. I only supposed that the more apt approach would be to use a dependency property.
"As beings of finite lifespan, our contributions to the sum of human knowledge is one of the greatest endeavors we can undertake and one of the defining characteristics of humanity itself"
|
|
|
|
|
Not when he already set the DataContext. The issue was purely a timing issue and could be fixed with a FallbackValue.
This space for rent
|
|
|
|
|
If you check the timing of what's happening, the bind is happening as the control is being created - it happens in the InitializeComponent call in the user control. What you could do here is supply a FallbackValue so that the binding is satisfied until the point that your ViewModel is created.
This space for rent
|
|
|
|
|
Changing / setting the DataContext invokes the binding mechanism.
public MyControl()
{
OnOffValues = new List<bool>();
for (int x = 0; x < 24; x++)
{
OnOffValues.Add(true);
}
InitializeComponent();
this.DataContext = this;
}
And there is no "ElementName" in play here.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
I have problem dragging file to the explorer.
It seems to be something to do with my current application settings.
I have a test app with hardcoded file and when the appropriate mouse event happen I trigger the following code (with hard coded file path)
var dragObj = new DataObject();
dragObj.SetFileDropList(new StringCollection() { @"H:\Temp\popeye.svg" });
DragDrop.DoDragDrop(new Window(), dragObj, DragDropEffects.Copy);
however if I do that in my other app (and I do it from the UI thread as well and with the same hard coded paths), nothing is dropped and the explorer show the forbidden cursor and the operation only complete when my mouse enter my window rect again.
I am stomped.. what could cause this code to work in one app, but not the other?
Remark: I am using the same version of the .NET Framework in both test and real app.
[EDIT]
Newsflash: this seems to be related to the UIElement I drag from being a drop target as well.
When I commented out the Drop event handlers, it dragged successfully.
Tricky, I need to be able to drop to it as well... mm....
[EDIT 2]
In my drop code I added some UIElement.MouseCapture() call. this was what prevented my code from working!
modified 21-Nov-18 11:06am.
|
|
|
|
|