I am trying to implement MVVM, and are having issues with moving LoadOnDemand to my ViewModel using triggers and RelayCommands, I have the event firing and all,
but as it turns out it is possible to expand a node in the tree without having it selected (i have databound the SelectedItem property in my ViewModel), thus breaking the logic, since the onLoad animation will continue to spin.
If I instead do this:
private void HierarchyTreeControl_LoadOnDemand(
Object sender,
Telerik.Windows.RadRoutedEventArgs e){
RadTreeViewItem clickedItem = null;
clickedItem = e.OriginalSource as RadTreeViewItem;
if (clickedItem != null) {
...do load logic
in the code behind file. I have access to the expanding item (clickedItem). What am I missing?
Is it possible to do some sort of binding on the ExandingItem?
Any Help will be appreciated :)
Since you are not using a standard treeview, I cannot be sure this is relevant. But I have previously had success in binding a TreeViewItem's IsExpanded property to a viewmodel property, in which I loaded items when the value was set to true (and not already loaded).
Here is a useful link: One more platform difference more-or-less tamed
Related
I run often into many problems which leads to refactoring my code...
That is why I want to ask for some recommendations.
The problems I'm running into are:
1) Providing data to XAML
Providing simple data to control value instead of using a value converter. For instance I have a color string like "#FF234243" which is stored in a class. The value for the string is provided by a web application so I can only specify it at runtime.
2) UI for every resolution
In the beginnings of my learning I got told that you can create a UI for every possible resolution, which is stupid.
So I've written a ValueConverter which I bind on an element and as ConverterParameter I give a value like '300' which gets calculated for every possible resolution... But this leads to code like this...
<TextBlock
Height={Binding Converter={StaticResource SizeValue}, ConverterParameter='300'}
/>
3) DependencyProperties vs. NotifyProperties(Properties which implement INotifyPropertyChanged) vs. Properties
I have written a control which takes a list of value and converts them into Buttons which are clickable in the UI. So I did it like this I created a variable which I set as DataContext for this specific Control and validate my data with DataContextChanged but my coworker mentioned that for this reason DependencyProperties where introduced. So I created a DependecyProperty which takes the list of items BUT when the property gets a value I have to render the buttons... So I would have to do something like
public List<string> Buttons
{
get { return (List<string>)GetValue(ButtonsProperty); }
set
{
SetValue(ButtonsProperty, value);
RenderButtons();
}
}
// Using a DependencyProperty as the backing store for Buttons. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonsProperty =
DependencyProperty.Register("Buttons", typeof(List<string>), typeof(MainPage), new PropertyMetadata(""));
private void RenderButtons()
{
ButtonBar.Children.Clear();
ButtonBar.ColumnDefinitions.Clear();
if(Buttons != null)
{
int added = 0;
foreach (var item in Buttons)
{
var cd = new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) };
var btn = new Button() { Content = item };
ButtonBar.ColumnDefinitions.Add(cd);
ButtonBar.Children.Add(btn);
Grid.SetColumn(btn, added);
}
}
}
And have to use it like this:
<Controls:MyControl
x:Name="ButtonBar" Button="{Binding MyButtons}">
</Controls:MyControl>
Since these are a lot of topics I could seperate those but I think that this is a pretty common topic for beginners and I have not found a got explanation or anything else
1. Providing data to XAML
There are two options: prepare data in the ViewModel or to use converter.
To my mind using converter is better since you can have crossplatform viewModel with color like you mentioned in your example and converter will create platform dependent color. We had similar problem with image. On android it should be converted to Bitmap class, while on UWP it's converted to BitmapImage class. In the viewModel we have byte[].
2. UI for every resolution
You don't need to use converter, since Height is specified in effective pixels which will suit all the required resolutions automatically for you. More info can be found at the following link:
https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
There are two options how to deal with textblock sizes:
a) Use predefined textblock styles and don't invent the wheel (which is the recommended option):
https://learn.microsoft.com/en-us/windows/uwp/design/style/typography#type-ramp
Or
b) Specify font size in pixels. They are not pixels, but effective pixels. They will be automatically scaled on different devices:
https://learn.microsoft.com/en-us/windows/uwp/design/style/typography#size-and-scaling
Furthermore, use adaptive layout to have different Layout for different screen sizes.
3) DependencyProperties vs. NotifyProperties(Properties which implement INotifyPropertyChanged) vs. Properties
As per your code you can try to use ListView or ItemsControl and define custom item template.
DependencyProperties are created in DependencyObject and are accessible in xaml. All controls are inherited from DependencyObjects. Usually you create them when you want to set them in xaml. They are not stored directly in the objects, but in the global dictionary and resolved at runtime.
DependencyProperties were created long time ago and you can find lots of links which explain them in details:
http://www.wpftutorial.net/dependencyproperties.html
https://techpunch.wordpress.com/2008/09/25/wpf-wf-what-is-a-dependency-property/
When should I use dependency properties in WPF?
What is a dependency property? What is its use?
What is a dependency property?
INotifyPropertyChanged INPC are the central part of MVVM. You bind your view to viewModel which implements INPC and when you change value of the property control is notified and rereads the new value.
Download the following video in high resolution which explains MVVM in details (by Laurent Bugnion):
https://channel9.msdn.com/Events/MIX/MIX11/OPN03
MVVM: Tutorial from start to finish?
Normal properties are used in model classes or when there is no need to notify UI regarding changes.
I am making a UWP app which is supposed to be on xbox for now and maybe in future ill release it on pc and other platforms. I know that on PC and for mobile we can enable this feature with following 2 properties on the GridView or ListView.
CanReorderItems=True
CanDrop=True
But according to Microsoft Docs, drag and drop feature is not available or supported on xbox.
So what are any other options to achieve this reorder feature on xbox GridView?
UPDATE 1
So here is my backend code for the gridview. selection mode is single but I am not using selectionchanged event because that just creates lot of confusion and for now just assume that we always need to swap the items I will set the boolean later once the swapping in working perfectly.
private void SamplePickerGridView_ChoosingItemContainer(Windows.UI.Xaml.Controls.ListViewBase sender, ChoosingItemContainerEventArgs args)
{
if (args.ItemContainer != null)
{
return;
}
GridViewItem container = (GridViewItem)args.ItemContainer ?? new GridViewItem();
//should be xbox actually after pc testing
if (DeviceTypeHelper.GetDeviceFormFactorType() == DeviceFormFactorType.Desktop)
{
container.GotFocus += Container_GotFocus;
container.LostFocus += Container_LostFocus;
//container.KeyDown += Container_KeyDown;
}
args.ItemContainer = container;
}
private TVShow GotItem, LostItem;
private void Container_LostFocus(object sender, RoutedEventArgs e)
{
LostItem = OnNowAllGridView.ItemFromContainer(e.OriginalSource as GridViewItem) as TVShow;
GotItem = null;
}
private void Container_GotFocus(object sender, RoutedEventArgs e)
{
GotItem = OnNowAllGridView.ItemFromContainer(e.OriginalSource as GridViewItem) as TVShow;
if (GotItem != null && LostItem != null)
{
var focusedItem = GotItem;
var lostitem = LostItem;
var index1 = ViewModel.Source.IndexOf(focusedItem);
var index2 = ViewModel.Source.IndexOf(lostitem);
ViewModel.Source.Move(index1, index2);
}
LostItem = null;
}
u can try the code with adaptivegridview or just normal gridview of uwp if it works with that it should work with adaptivegridview as well.
Current Bheaviour items are swaped but the focus remains at same index.
Expected the focus should also move along with the item.
Your finding is true, drag and drop is not supported on Xbox out of the box (although when mouse support comes to Xbox in the future, I guess it will work).
So if you need this functionality, you will have to implement it manually from the start. One option would be to add a button, that will display on Xbox only and will read like Reorder Grid.
When this "reorder" mode were enabled, you have several solutions available.
The easiest solution for you would be to set the SelectionMode to Single and when a item is selected, you would bring it to fromt of the underlying collection.
collection.Remove( selectedItem );
collection.Insert( 0, selectedItem );
This bring to front solution was implemented on the Xbox One dashboard for reordering tiles.
Second option would be to set the SelectionMode to Multiple, where user would first select one item and then a second one. After that you could move the first selected item before the second selected:
collection.Remove( firstSelectedItem );
var targetIndex = collection.IndexOf( secondSelectedItem );
collection.Insert( targetIndex, firstSelectedItem );
The last solution is the most complex. With SelectionMode = Single you would select a single item and then observe the direction in which the user focus moves and move the tile "in real time". This is the most user friendly, but hardest to implement reliably.
Just as an outline of the third solution - you could capture the GotFocus event if you implement a custom template of the GridView:
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal"
GotFocus="GridViewItem_GotFocus"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
Now within this GotFocus handler you could retrieve the item that has currently focus from the EventArgs.OriginalSource. This way you could know which item got the focus and you could swap it with the item the user selected.
Update - hacky solution
I have come up with a hacky approach that solves the GotFocus/LostFocus mess.
The problem with GotFocus is that when we move the item in collection, the focus gets confused. But what if we didn't physically move the items at all?
Suppose your item type is TVShow. Let's create a wrapper around this type:
public class TVShowContainer : INotifyPropertyChanged
{
private TVShow _tvShow;
public TVShow TvShow
{
get => _tvShow;
set
{
_tvShow = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now change the collection item type to this new "wrapper" type. Of course, you also have to update your GridView DataTemplate to have the right references. Instead of "{Binding Property}" you will now need to use "{Binding TvShow.Property}", or you can set the DataContext="{Binding TvShow}" attribute to the root element inside the DataTemplate.
But you may now see where I am going with this. Currently you are using Move method to move the items in the collection. Let's replace this with a swap:
var item1 = focusedItem.TvShow;
focusedItem.TvShow = LostItem.TvShow;
LostItem.TvShow = item1;
This is a big difference, because we no longer change the collection itself, but just move the references to items that are wrapped in a "static" container. And thanks to bindings the items will properly display where they should.
This is still a hacky solution, because it requires you to wrap your items just for the sake of the reordering, but it at least works. I am however still interested in finding a better way to do this.
I Build a scrollViewer and its elements in my ViewModel, and it's built into a property FrameworkElement PageElement I rebuild the pageElement every time some event happens, I want to bind the PageElement to a real scrollViewer in the View so that whenever I change pageElement, it draws itself in it's view.
Let me give you a little armchair advice. I don't know the details of your project but the details in your question make me draw a few conclusions.
First, to have your view model create UI elements is not wrong. But it is really unusual. It sounds like you might be missing the concept of data template or data template selector.
Using a data template allows you to have a rich presentation of data that is built as the individual record is generated and rendered in a repeater or in a single content control.
Using a data template selector allows you to have various different presentations of data that using code-behind logic will switch between based on data or other criteria.
Ref on templates: http://blog.jerrynixon.com/2012/08/windows-8-beauty-tip-using.html
Second, to have your UI be re-generated as the result of an event being raised sounds like a short path to performance problems.
Every time you manually create elements and add them to the visual tree, you put your app at risk of binding lag while the layout is re-rendered. Run your app on an ARM and I bet you may already see it. Then again, a simplistic UI may not suffer from this general rule of thumb.
Because I do not know the event, I cannot presume it is frequently occurring. However, if it is frequently occurring, then even a simplistic UI will suffer from this.
Now to answer your question
Sherif, there is no write-enabled property on a scrollviewer that will set the horizontal or vertical offset. The only way to set the offset of a scrollviewer is to call changeview().
var s = new ScrollViewer();
s.ChangeView(0, 100, 0);
You cannot bind to a method, so binding to something like this is a non-starter without some code-behind to read the desired offset and calling the method directly.
Something like this:
public sealed partial class MainPage : Page
{
MyViewModel _Vm = new MyViewModel();
ScrollViewer _S = new ScrollViewer();
public MainPage()
{
this.InitializeComponent();
this._Vm.PropertyChanged += (s, e) =>
{
if (e.PropertyName.Equals("Offset"))
_S.ChangeView(0, _Vm.Offset, 0);
};
}
}
public class MyViewModel : INotifyPropertyChanged
{
private int _Offset;
public int Offset
{
get { return _Offset; }
set
{
_Offset = value;
PropertyChanged(this, new PropertyChangedEventArgs("Offset"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
But let me caution you. The offset will need to be based on something. And those variables may change based on the window size, the font size, scaling from transforms, and lots of other factors. The code above will work most of the time, but it will possible fail frequently on other devices.
So, what to do? My recommendation is that you code this in your code-behind, monitoring for whatever scenario you feel would require a scroll, and simply programmatically scroll it from bode-behind. Beware, though, programmatically scrolling a scrollviewer could make your UI confusing to the user.
You know your app. You will have to choose.
Best of luck!
Working on a Metro style app in C#. I have a custom control which inherits from Grid. MyGrid contains some other custom controls. I'm trying to do hit testing on those controls in the PointerReleased handler:
void MyGrid_PointerReleased(object sender, PointerRoutedEventArgs e)
{
PointerPoint pt = e.GetCurrentPoint(this);
var hits = VisualTreeHelper.FindElementsInHostCoordinates(pt.Position, this);
int breakhere = hits.Count();
}
After this code executes, hitCount is 0. If I move the PointerReleased handler one control higher in the visual tree heirarchy then hitCount is correct the first time and 0 after that. I set up a test project with similar XAML layout to try to reproduce the problem and it works correctly every time. So I'm not sure what bad thing I have done that is preventing VisualTreeHelper from working. I'm not really sure how to proceed debugging this. Any ideas what would cause this function to return no results?
I am designing a settings form for my application as shown below:
A tree view with multiple nodes at the left and I want to have one GroupBox for each node to be displayed at the right whenever a node is selected. I have designed my group box with necessary controls for the first node. The question is, how do I design an another group box in the same place when another item is already there. Is there a way to hide a control from a form during design time?
I have always just changed the Z-Order of the GroupBox or Panel by right clicking on it and sending it to back. Just make sure when you add the other GroupBoxes that you add them to the same Parent. Make their Visible property False and then display the GroupBox you want at runtime by making it Visible.
EDIT: Changed answer to be more relevant.
EDIT #2: I missed the VB tag, translating this should be a trivial task anyway.
If you extend GroupBox like this, you'll have a stock GroupBox which will hide itself at design time.
public class myGroupBox : GroupBox
{
public myGroupBox() { InitializeComponent(); }
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (DesignMode) this.Visible = false;
}
}
NOTE: This should work for almost any non-sealed control.