I am currently working on a WPF project with the Telerik FW.
During the runtime I am getting the following warning:
System.Windows.Freezable Warning: 1 : CanFreeze is returning false because a DependencyProperty on the Freezable has a value that is an expression; Freezable='System.Windows.Media.TranslateTransform'; Freezable.HashCode='36319496'; Freezable.Type='System.Windows.Media.TranslateTransform'; DP='X'; DpOwnerType='System.Windows.Media.TranslateTransform'
This is my xaml code
<Style x:Key="PieSliceStyle" TargetType="Path">
<Setter Property="Fill" Value="{Binding DataItem.Color}" />
</Style>
<telerik:PieSeries ItemsSource="{Binding Source}" DefaultSliceStyle="{StaticResource PieSliceStyle}">
<telerik:PieSeries.ValueBinding>
<telerik:PropertyNameDataPointBinding PropertyName="Value" />
</telerik:PieSeries.ValueBinding>
<telerik:PieSeries.LabelDefinitions>
<telerik:ChartSeriesLabelDefinition Margin="-10">
<telerik:ChartSeriesLabelDefinition.Binding>
<telerik:PropertyNameDataPointBinding PropertyName="Label" />
</telerik:ChartSeriesLabelDefinition.Binding>
</telerik:ChartSeriesLabelDefinition>
</telerik:PieSeries.LabelDefinitions>
</telerik:PieSeries>
And this is some part of my ViewModel
private readonly SolidColorBrush PieColorEnableSlice = new SolidColorBrush(Colors.LightGray);
private readonly SolidColorBrush PieColorDisabledSlice = new SolidColorBrush(Colors.Red);
public AsyncObservableCollection<MSShareClassModel> List
{
get
{
return this._list;
}
set
{
if (this.SetProperty(ref this._list, value, "List"))
{
this.Source = new AsyncObservableCollection<PieChartModel>
{
new PieChartModel
{
Label = "Active",
Value = this._list.Count(x => x.Status == "1"),
Color = this.PieColorEnableSlice
},
new PieChartModel
{
Label = "Disable",
Value = this._list.Count(x => x.Status == "0"),
Color = this.PieColorDisabledSlice
},
};
}
}
}
I think one solution would be to create the corlor directly from the xaml source.
But I want to keep this binding to be able to change the color programatically.
Any idea on this warning?
Ok After more investigation, this is not comming from the pieSeries..
This is fired by the RadGridView....
I removed all the xaml components one by one from the xaml.
The last one alive was the grid and I keeped having that warning.
I removed the grid and enabled all the others components, and the warning was gone until I uncomment the grid in the xaml.
Nothing fancy, just the simple RadGridView declaration. No DataSource or Column definition, just a simple empty grid.
This issue seems to have been declared to telerik dev team since 2010 from many components. (Treeviews, grid etc...)
And after some reading, Telerik will not fix this kind of issue... (we can vote for it on http://feedback.telerik.com/ :) )
I will not make this as an answer, I still have the warning =/
Related
My existing c# WPF project with OxyPlot.Wpf v2.0 NuGet Package runs fine and uses Data binding.
OxyPlot.Wpf 2.1.2 is out but I have problems that the data binding no longer processes updates for use as Live-Chart.
To test, I wrote 2 test programs to clarify that.
In both versions INotifyPropertyChanged is implemented and I use the following properties for my data:
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
...
private ObservableCollection<DataPoint> valueList1;
public ObservableCollection<DataPoint> ValueList1
{
get => valueList1;
set
{
valueList1 = value;
OnPropertyChanged();
}
}
private ObservableCollection<DataPoint> valueList2;
public ObservableCollection<DataPoint> ValueList2
{
get => valueList2;
set
{
valueList2 = value;
OnPropertyChanged();
}
}
In the constructor, I add a few test data that can also be seen directly in the OxyPlot Chart when it starts in both OxyPlot versions. So the data binding should be fine with that.
I add further data to the ObservableCollection Propertys by clicking a button.
In Version 2.0 the WPF-Chart updates and shows the new Data.
In Version 2.1 nothing happens, data binding doesn't seem to trigger any updates...
As part of the update to OxyPlot.Wpf v2.1, I have to move my XAML code to the code behind:
XAML v2.0
<oxy:Plot Title="OxyTest" >
<oxy:Plot.Axes>
<oxy:LinearAxis MaximumPadding="0.1" IsZoomEnabled="True" MajorStep="100" />
</oxy:Plot.Axes>
<oxy:Plot.Series>
<oxy:AreaSeries Color="Red" Title="DataLine1" ItemsSource="{Binding Path=ValueList1}" />
<oxy:LineSeries Color="Blue" Title="DataLine2" ItemsSource="{Binding Path=ValueList2}" />
</oxy:Plot.Series>
</oxy:Plot>
Code Behind v2.1
MyModel = new PlotModel
{
Title = "OxyTest",
};
MyModel.Axes.Add(new LinearAxis
{
MaximumPadding = 0.1,
IsZoomEnabled = true,
MajorStep = 100
});
MyModel.Series.Add(new AreaSeries
{
Color = OxyColor.FromRgb(255, 0, 0),
Title = "DataLine1",
ItemsSource = ValueList1
});
MyModel.Series.Add(new LineSeries
{
Color = OxyColor.FromRgb(0, 0, 255),
Title = "DataLine2",
ItemsSource = ValueList2
});
EDIT:
The InvalidatePlot() method must be called for the display to be updated. The method has existed for a long time but in version 2.0 I didn't have to trigger it manually!?
Thanks to this Article How to refresh oxyplot plot when data changes
The Solution from heltonbiker looks good so i added this in my project:
ValueList1.CollectionChanged += (a, b) => MyModel.InvalidatePlot(true);
ValueList2.CollectionChanged += (a, b) => MyModel.InvalidatePlot(true);
Maybe this will help someone else.
The offical OxyPlot documentation is labeld as under construction.
EDIT2:
I found another Solution for my DataBinding Problem.
Just add OxyPlot.Contrib.Wpf to the Project and change the XMLNS from "http://oxyplot.org/wpf" to "http://oxyplot.org/wpf/contrib" and use the Exact same XAML from v2.0. No need to trigger the CollectionChanged Event. Databinding works fine.
I am building a WPF app and figured I would use AutoMapper to make copies of objects in my viewmodels. The problem I am having is that appears that AutoMapper is attempting to use a bound control as a source value and I don't understand why. I am new to AutoMapper so I figure I am missing some detail.
Details
UI has a list box and three buttons (Add, Edit, Delete). If nothing is selected in the list box, then only the Add button is active.
If an item is selected in the list box then all buttons are active.
If the Add button is clicked, a new empty object is created with properties bound to text boxes in the UI.
If the Edit button is clicked, a copy of the item selected in the list box is made and the copy's properties are bound to the text boxes in the UI.
All of this works. The problem occurs if I try to use AutoMapper to make the copy.
Here is the code in question. I have included what works (doing the copy manually, property by property) and the code that fails when using AutoMapper.
There are three properties in the viewmodel involved:
// Bound to the list box's ItemsSource property
public ObservableCollection<CarType> CarTypes
{
get { return _carTypes; }
set { SetProperty(ref _carTypes, value); }
}
// Bound to the list box's SelectedIndex property
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
SetProperty(ref _selectedIndex, value);
}
}
// Properties of CarTypeDto are bound to text boxes in the Edit UI
public CarType CarTypeDto
{
get
{
// When the value is retrieved, a copy of the selected item
// is made and the copy is returned.
if (SelectedIndex != -1)
{
// What works==========
// CarType newDto = new CarType();
// CarType src = CarTypes[SelectedIndex];
// newDto.Id = src.Id;
// newDto.Name = src.Name;
// newDto.Description = src.Description;
// _carTypeDto = newDto;
// What does NOT work============
CarType src = CarTypes[SelectedIndex];
CarType newDto = _mapper.Map<CarType>(src); <=== fails here
_carTypeDto = newDto;
// Incorrect Solution #1 ======================
// Changed the above two lines to this solves the problem:
_mapper.Map<CarType, CarType>(src,_carTypeDto);
}
return _carTypeDto;
}
set
{
SetProperty(ref _carTypeDto, value);
}
}
When I try to use AutoMapper it gets to the line that fails and the debugger shows this error:
System.Windows.Data Error: 17 : Cannot get 'CarTypeDto' value (type 'CarType') from '' (type 'CarTypesViewModel'). BindingExpression:Path=CarTypeDto.Description; DataItem='CarTypesViewModel' (HashCode=66939890); target element is 'TextBox' (Name='DescriptionTextBox'); target property is 'Text' (type 'String')
What is confusing me is that I get a reference to the selected item (src) and attempt to map that to a new instance of CarType (newDto). Neither of these items (src or newDto) is part of the binding to the DescriptionTextBox in the editing UI. Why is the binding becoming an issue when I use AutoMaper?
The commented code (manually copying properties) works fine.
In case it helps, here are the bindings in question:
<ListBox x:Name="ContentView"
DockPanel.Dock="Top"
ItemsSource="{Binding CarTypes}"
Background="{StaticResource ControlBackgroundBrush}"
Foreground="{StaticResource ControlForegroundBrush}"
BorderBrush="{StaticResource ControlForegroundBrush}"
SelectedIndex ="{Binding SelectedIndex}" >
<TextBox Grid.Row="1"
Grid.Column="0"
Name="NameTextBox"
Margin="5"
Width="100"
MaxLength="10"
Text="{Binding CarTypeDto.Name, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBox Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
Name="DescriptionTextBox"
Margin="5"
Width="300"
MaxLength="30"
Text="{Binding CarTypeDto.Description,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
In the bootstrap portion of the app I initialize the mapper like so...
private void InitializeAutomapper()
{
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<CarType, CarType>();
});
_mapper = new Mapper(mapperConfig);
}
...and then inject _mapper into the viewmodels as needed. I have tested the mapper and it will correctly map objects. The issue shows up when I attempt to do the mapping in the CarTypeDto property shown above.
==============================
EDIT - Note about the solution
I wanted to add a note about the solution for folks who may read this later - especially folks newer to AutoMapper and property binding (like myself).
The answer below made me realize that my approach to populating the CarTypeDto property was not correct. In the final solution I revamped the property to this:
public CarType CarTypeDto
{
get { return _carTypeDto; }
set { SetProperty(ref _carTypeDto, value); }
}
Then, I update the value to the CarTypeDto property elsewhere when needed. For example when the Edit button is clicked, I then retrieve the selected value and map it to CarTypeDto. The code shown at the top of this post is not the appropriate way to populate the value for a bound property.
While all of this had nothing to do with AutoMapper, when I tried to use AutoMapper it brought the larger problem to light.
The error mentioned by you is a binding error not the Automapper exception message.
System.Windows.Data Error: 17 : Cannot get 'CarTypeDto' value (type 'CarType') from '' (type 'CarTypesViewModel'). BindingExpression:Path=CarTypeDto.Description; DataItem='CarTypesViewModel' (HashCode=66939890); target element is 'TextBox' (Name='DescriptionTextBox'); target property is 'Text' (type 'String')
Now about mapping is not working ---- Before using Automapper, the map should be created so that AutoMapper can understand how to map one object/type to another. In your scenario, since you need to type from same type then you should create a map like
_mapper.CreateMap<MyType, MyType>();
The CreateMap should be called in Automapper profile initialization.
Update on your binding error:
The binding error is appearing as you are trying to create a new Object of CarType everytime in Get property so CarTypeDto property is not pointing to the object which was bound initially at XAML. Instead of creating a new object by mapper you should work on same object and update the property.
In my C# WPF application I have a DataGrid and right above it there is a TextBox for the user to search and filter the grid as they type. If the user types fast though, no text will appear until 2 seconds after they type because the UI thread is too busy updating the grid.
Since most of the delay is all on the UI side (i.e. filtering the datasource is nearly instant, but rebinding and re-rendering the grid is slow), multi-threading has not been helpful. I then tried setting the dispatcher of just the grid to be at a lower level while the grid gets updated, but this didn't solve the issue either. Here's some code... Any suggestions on how to solve this type of problem?
string strSearchQuery = txtFindCompany.Text.Trim();
this.dgCompanies.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(delegate
{
//filter data source, then
dgCompanies.ItemsSource = oFilteredCompanies;
}));
Using a ListCollectionView as your ItemsSource for the grid and updating the Filter works much faster than re-assigning the ItemsSource.
The example below filters 100000 rows with no apparent lag by simply refreshing the View in the setter for the search term text property.
ViewModel
class ViewModel
{
private List<string> _collection = new List<string>();
private string _searchTerm;
public ListCollectionView ValuesView { get; set; }
public string SearchTerm
{
get
{
return _searchTerm;
}
set
{
_searchTerm = value;
ValuesView.Refresh();
}
}
public ViewModel()
{
_collection.AddRange(Enumerable.Range(0, 100000).Select(p => Guid.NewGuid().ToString()));
ValuesView = new ListCollectionView(_collection);
ValuesView.Filter = o =>
{
var listValue = (string)o;
return string.IsNullOrEmpty(_searchTerm) || listValue.Contains(_searchTerm);
};
}
}
View
<TextBox Grid.Row="0" Text="{Binding SearchTerm, UpdateSourceTrigger=PropertyChanged}" />
<ListBox ItemsSource="{Binding ValuesView}"
Grid.Row="1" />
If you are targeting .net 4.5, an option is to set the Delay property on your TextBox which will prevent setting the source value until a certain time threshold is met (until the user stops typing).
<TextBox Text="{Binding SearchText, Delay=1000}"/>
This waits for 1 second after there is no user input to set the source value.
Another option is to have a button trigger your filter/search instead of when the textbox changes.
In my program's main window I have a TreeView and a ContentPresenter. The display of the ContentPresenter is determined by what node is selected in the TreeView.
The name of one of my nodes is allowed to be changed by the user via contentMenu. All the user has to do is right click the node and select the new name out of the choices. The ContentPresenter is supposed to have a null display until the user chooses a name for the node.
The problem occurs when a new name is selected from the contentMenu. The ContentPresenter's display changes, like it should, but only after the user selects a different node (changing the display), and then re-selects the original node.
How do I make it so that the display on the ContentPresenter changes right when the TreeView node's name is changed?
TreeViewViewModel:
public class TreeViewViewModel : PropertyChangedBase
{
public TreeViewViewModel()
{
Node = new Node() { NodeName = "Blank", NodeDataModel = new NodeModel(),
Commands = { new Command(nodeType_name1), new Command(nodeType_name2) } };
}
//These functions call to the NodeName property in the TreeView's Data Model
private void nodeType_name1()
{
Node.NodeName = "Name1";
}
private void nodeType_name2()
{
Node.NodeName = "Name2";
}
}
XAML for MainWindow:
<!-- Tree view items & Functions -->
<TreeView Name="Tree_One" ItemsSource="{Binding DataTree.Data}" ... >
<TreeView.Resources>
<SolidColorBrush Color="LightSkyBlue" x:Key="{x:Static SystemColors.HighlightBrushKey}" />
</TreeView.Resources>
</TreeView>
<!--- Left Widget -->
<ContentPresenter Content="{Binding LeftWidget}" />
MainWindowViewModel:
public class MainWindowViewModel : PropertyChangedBase
{
private TreeViewViewModel _dataTree;
public MainWindowViewModel()
{
_dataTree = new TreeViewViewModel();
}
public TreeViewViewModel DataTree { ... }
//This function is in charge of changing the display of the ContentPresenter
// I think that my problem can probably be solved by doing something here
public void ChangeViews()
{
if (_dataTree.SelectedItem is Node)
{
var _node = _dataTree.SelectedItem as Node;
var nodeViewModel = new NodeViewModel(_node.NodeDataModel);
if (_node.NodeName== "Unknown")
LeftWidget = null; //This is the Content Presenter **
if (_node.NodeName == "Name1")
{
LeftWidget = nodeViewModel;
}
if (_node.NodeName == "Name2") {...}
}
}
}
Duh, thats a alot of code and its pretty difficult to understand what you up to since you seem to have controls in your ViewModel.
Or at least it looks to me that you have them in ViewModel. That is not very MVVM-alike my friend. :)
"The problem occurs when a new name is selected from the contentMenu. The ContentPresenter's display changes, like it should, but only after the user selects a different node (changing the display), and then re-selects the original node."
The property changed is not being fired because the new selected value is equal to the old one.
Pretty obvious, right?... no property was actually changed
But why do you want the ContentPresenter to update itself with the value that it already has?
You said when you select a node the ContentPresenter displays it properly and when you re-select the same the ContentPresenter is not doing anything.
Its not doing anything because it think it doesnt need to. Which is true.
So the question is why would you make ContentPresenter force to refresh on each value no matter if old value is the same as new one?
Though if you want to hack/trick a little bit, you can always set ContentPresenter's Content to null before you assign another value. :)
However, post us more code and we will be able to provide you a better solution to your issue.
I was able to fix this issue by calling ChangeViews(); in my MainWindowViewModel from my TreeViewViewModel. I did this by using a delegate property in the TVVM, and adding it to my MWVM. By doing this, the display is updated whenever ChangeViews(); is called.
This is the answer that I used.
I would like to create a UserControl containing a DataGrid and then define the columns directly inside my UserControl:
<my:ControlContainingDataGrid ItemsSource="{Binding}">
<my:ControlContainingDataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Property1}" Header="Property 1"/>
In the UserControl I expose the columns of the DataGrid :
static ControlContainingDataGrid()
{
ColumnsProperty = DependencyProperty.Register(
"Columns",
typeof(ObservableCollection<DataGridColumn>),
typeof(ControlContainingDataGrid),
new UIPropertyMetadata(new ObservableCollection<DataGridColumn>())
);
}
[Description("Columns"), Category("Columns")]
public ObservableCollection<DataGridColumn> Columns
{
get { return _datagGrid.Columns; }
}
public static readonly DependencyProperty ColumnsProperty;
=> it doesn't work : the column binded to Property1 is not created.
I try to create the column programatically :
_datagGrid.Columns.Add(new DataGridTextColumn {
Header = "Property 1",
Binding = new Binding {
Path = new PropertyPath("Property1"),
Mode = BindingMode.TwoWay,
},
});
_datagGrid.ItemsSource = testList;
=> it doesn't work : the header is displayed but each row of my DataGrid is empty (bad binding ?).
1- What is the simpliest way to bind the columns of a DataGrid via the UserControl in the XAML part ?
2- What is the simpliest way to bind the columns of a DataGrid via the UserControl programatically ?
I do this programmatically. Pretty much the same code as you have. Works fine. I have never tried to bind the Columns collection itself though I don't see why that should not work.
Your message is a little ambiguous. If you mean to say that your column does not show up, maybe you need to add the columns prior to creating the table. If the rows show up with empty value in the column, then your binding 'Property1' is wrong.
I don't work in XAML so don't know nothing about that.