Caliburn Micro: How to add text to the bottom of a list box and display it - caliburn.micro

I'm trying to figure out how to add text to the bottom of a list box and display it. In WPF with code behind, I would grab the ScrollViewer and manipulate it, but I can't figure out how to do it with Caliburn...

You have a couple options.
1) In your ViewModel you can call GetView and cast it to your view type and get a reference to the ScrollViewer. Something like:
var myView = this.GetView() as MyView;
var myScrollView = myView.MyScrollView;
That works fine but isn't ideal if you're trying to not couple the view to the view model.
Option 2) is to implement IResult, see docs here.
public class ScrollViewResult : IResult
{
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
private ScrollViewResult ()
{
}
public void Execute (ActionExecutionContext context)
{
var view = context.View as FrameworkElement;
var scrollViewer = FindVisualChild<ScrollViewer>(view);
//do stuff to scrollViewer here
Completed (this, new ResultCompletionEventArgs { });
}
private static TChildItem FindVisualChild<TChildItem> (DependencyObject obj)
where TChildItem : DependencyObject
{
for (var i = 0; i < VisualTreeHelper.GetChildrenCount (obj); i++)
{
var child = VisualTreeHelper.GetChild (obj, i);
if (child != null && child is TChildItem)
return (TChildItem)child;
var childOfChild = FindVisualChild<TChildItem> (child);
if (childOfChild != null)
return childOfChild;
}
return null;
}
//this isn't required of course but comes in handy for
//having a static method and passing parameters to the
//ctor of the IResult
public static IResult DoSomething ()
{
return new ScrollViewResult ();
}
Then you can call it like:
public IEnumerable<IResult> SomeAction()
{
yield return ScrollViewResult.DoSomething();
}

Related

Can I use ClassId in tabbedpage to differentiate their content

I'm trying to use the same page for 3 tabs in TabbedPage. But each tab has to display different data in the listview. Is there a way to set a parameter for each tab?
Example
<local:Sales Title="Pending"
Icon="ic_shortcut_home.png"
ClassId="pending"/>
<local:Sales Title="Posted"
Icon="ic_shortcut_home.png"
ClassId="posted"/>
<local:Sales Title="Uploaded"
Icon="ic_shortcut_home.png"
ClassId="uploaded"/>
I tried using ClassId, and Title to get their difference but I'm having trouble retrieving the ClassId in the Sales class constructor, are there any other ways to get the output I want?
public Sales()
{
InitializeComponent();
BindingContext = this;
salesCollection = new ObservableCollection<Head>();
initLvw();
Console.WriteLine(Title); //returns null
Console.WriteLine(ClassId); // returns null
}
You can load data in OnAppearing method:
protected override void OnAppearing()
{
base.OnAppearing();
Console.WriteLine(ClassId + "OnAppearing");
BindingContext = this;
salesCollection = new ObservableCollection<Head>();
initLvw();
}
Or load data with a little delay in constructor:
public AboutPage()
{
InitializeComponent();
Task.Run(async () =>
{
await Task.Delay(200);
Console.WriteLine(ClassId + "AboutPage");
BindingContext = this;
salesCollection = new ObservableCollection<Head>();
initLvw();
});
}

Making a class Async for WinRT / Windows 8 Developement

I'm developing a Win 8 app which needs to do some possibly long running looping and calculations based around the data input by the user. This calculation is run often to update the results in real time.
The calculations are done by a calculator class. I will use example code to give an idea
public class ResultCalculator
{
List<Data> Input1 = new List<Data>();
IQueryable<int> Input2;
IQueryable<Data2> Input3;
public ResultCalculator(List<int> items1, List<Data2> items2)
{
items1.Sort((x,y) => y.CompareTo(x));
Input2 = items1.AsQueryable();
Input3 = items2.AsQueryable().OrderByDescending(w => w.LValue);
}
public void CalculateLValues()
{
foreach (var v in Input3)
{
for (int i = 1; i <= v.Quantity; i++)
{
if (Input1.Count > 0)
{
Data existing = FindExisting(v.LValue, 4);
if (existing != null)
{
existing.Add(v.LValue);
}
else
{
FindNew(v.LValue);
}
}
else
{
FindNew(v.LValue);
}
}
}
OptimisePass1();
}
public void FindNew(int LValue)
{
int options = FindNewItem(LValue, 0);
if (options != 0)
{
Data newdata = new Data(options);
newdata.Add(LValue);
Input1.Add(newdata);
}
}
public void OptimisePass1()
{
foreach (var w in Input1)
{
var ShorterLValues = from sl in Input2 where sl < w.LValue orderby sl select sl;
foreach (var sl in ShorterLValues)
{
if (sl > w.LValue - w.Remaining)
{
w.LValue = sl;
w.CalculateRemaining();
}
}
// MORE CALCULATION TO DO IN ANOTHER LOOP
}
}
public Data FindExisting(int LValueRequired, int additionalvalue)
{
Input1.OrderBy(w => w.LValue);
foreach (var sl in Input1.Where(i => i.Remaining > 0).OrderBy(i => i.Remaining))
{
if (sl.Remaining >= LValueRequired + additionalvalue)
{
return sl;
}
}
return null;
}
public int FindNewItem(int LValueRequired)
{
foreach (var sl in Input2)
{
if (sl >= LValueRequired + 4)
{
return sl;
}
}
return 0;
}
}
This class is the used from my ViewModel as below...
public async Task UpdateCalculationAsync()
{
var data1 = new List<int>(sInput);
var reqlist = new List<Data2>(sInput2);
var lc = new ResultCalculator(data1, data2);
NL.Clear();
await Task.Run(() => lc.CalculateLValues());
foreach (var i in lc.Data1)
{
NL.Add(i);
}
}
Without any async use this held up the UI when it ran if there were many items in the lists. So I added the "await Task.Run(() => lc.CalculateLValues())" to make it run async. I've got a basic grasp of async but not really understood how to properly make my classes run in async. Is this approach correct?
I believe that what I've done hands off the calculation to the background thread. Certainly the UI now remains responsive and can be used whilst the calculation is running. Once the result is calculated my viewmodel gets the result and the UI updates. What I'd really rather have is for my ResultCalculator class to have Task returning methods which I can await. However I'm really struggling on how to refactor for that. I'm not even sure there's a need if this works and is a valid approach. But I'm not 100% convinced it is the proper use of the async pattern and wanted to check if it can be improved?
suggest to do something like this:
// user will do some action on the UI to trigger the compute
public async Task Button1_Click(..)
{
await this.ViewModel.RecalculateAsync();
}
class ViewModel
{
public async Task RecalculateAsync()
{
var data1 = new List<int>(sInput);
var reqlist = new List<Data2>(sInput2);
var lc = new ResultCalculator(data1, data2);
await Task.Run(() => lc.CalculateLValues());
// I am not sure that is NL - if it is items like property on viewmodel that is bound to ListView like control in xaml - that should be fine.
// otherwise, add code here to add the list of result items to an observable collection
// expose this collection as Items property on viewmodel and bind in xaml to ListView.ItemsSource
}
}

Strangeness with DataContext and GridView / ListView

I have a Windows 8 store app based off of the grouped template project, with some renames etc. However, I'm having a hard time getting the ItemsSource databinding to work for both non-snapped and snapped visual states.
I have a property, that, when set, changes the ItemsSource property, but I can only get one of the controls to bind at a time (either the GridView for non-snapped, or the ListView for snapped).
When I use the following, only the non-snapped binding works and the snapped binding shows no items:
protected PickLeafModel ListViewModel
{
get
{
return (PickLeafModel)m_itemGridView.ItemsSource;
}
set
{
m_itemGridView.ItemsSource = value;
m_snappedListView.ItemsSource = value;
}
}
If I comment out one of the setters, the snapped view shows items but the non-snapped view shows nothing:
protected PickLeafModel ListViewModel
{
get
{
return (PickLeafModel)m_itemGridView.ItemsSource;
}
set
{
//m_itemGridView.ItemsSource = value;
m_snappedListView.ItemsSource = value;
}
}
It's as if I can bind my view model only to one property at a time. What am I doing wrong?
Since I am generating my data model on another thread (yes, using the thread pool), I cannot make it inherit from DependencyObject. If I do, I get a WrongThreadException.
So to make it work I have done the following:
public class PickLeafModel : IEnumerable
{
public PickLeafModel()
{
}
public IEnumerator GetEnumerator()
{
if (m_enumerator == null)
{
m_enumerator = new PickLeafModelViewDataEnumerator(m_data, m_parentLeaf);
}
return m_enumerator;
}
private SerializableLinkedList<PickLeaf> m_data =
new SerializableLinkedList<PickLeaf>();
}
and then my items look like this:
// Augments pick leafs by returning them wrapped with PickLeafViewData.
class PickLeafModelViewDataEnumerator : IEnumerator
{
public PickLeafModelViewDataEnumerator(
SerializableLinkedList<PickLeaf> data, PickLeaf parentLeaf)
{
m_viewDataList =
new System.Collections.Generic.LinkedList<PickLeafViewData>();
foreach (PickLeaf leaf in data)
{
PickLeafViewData viewData = new PickLeafViewData();
viewData.copyFromPickLeaf(leaf, parentLeaf);
m_viewDataList.AddLast(viewData);
}
m_enumerator = m_viewDataList.GetEnumerator();
}
public void Dispose()
{
m_viewDataList = null;
m_enumerator = null;
}
public object Current
{
get
{
return m_enumerator.Current;
}
}
public bool MoveNext()
{
return m_enumerator.MoveNext();
}
public void Reset()
{
m_enumerator.Reset();
}
private IEnumerator<PickLeafViewData> m_enumerator = null;
private System.Collections.Generic.LinkedList<PickLeafViewData>
m_viewDataList;
}
}
Is there something I'm doing fundamentally wrong?
Help appreciated.
Thanks!
Thankfully there is a much easier way to do what you are trying!
Create a class called your ViewModel as shown below:
public class DataViewModel
{
public DataViewModel()
{
Data = new ObservableCollection<PickLeafViewData>(new PickLeafModelViewDataEnumerator(m_data, m_parentLeaf));
}
public ObservableCollection<PickLeafViewData> Data
{
get;
set;
}
}
Now on the code behind set the Page.DataConected to equal an instance of the above class.
And finally on both your snapped listview, and the grid view set the item source to this:-
ItemsSource="{Binding Data}"
That should work nicely for you.
Thanks to Ross for pointing me in the right direction.
I'm not 100% happy with this solution, but it does work. Basically the idea is that after I get back the PickLeafModel from the worker threads, I transplant its internal data into a derived version of the class which is data binding aware.
public class PickLeafViewModel : PickLeafModel, IEnumerable
{
public PickLeafViewModel()
{
}
public PickLeafViewModel(PickLeafModel model)
{
SetData(model);
}
public void SetData(PickLeafModel model)
{
model.swap(this);
}
public IEnumerator GetEnumerator()
{
if (m_observableData == null)
{
m_observableData = new ObservableCollection<PickLeafViewData>();
var data = getData();
PickLeaf parentLeaf = getParentLeaf();
foreach (PickLeaf leaf in data)
{
PickLeafViewData viewData = new PickLeafViewData();
viewData.copyFromPickLeaf(leaf, parentLeaf);
m_observableData.Add(viewData);
}
}
return m_observableData.GetEnumerator();
}
and the page code is as follows:
protected PickLeafViewModel ListViewModel
{
get
{
return DataContext as PickLeafViewModel;
}
set
{
DataContext = value;
}
}
whenever I want to set ListViewModel, I can do this:
ListViewModel = new PickLeafViewModel(model);
and swap looks like:
private static void swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
// Swaps internals with the other model.
public void swap(PickLeafModel other)
{
swap(ref m_data, ref other.m_data);
...
Also, PickLeafModelViewDataEnumerator can be deleted altogether.

In ASP.NET, can I convert a bunch of dropdowns into an array of dropdowns?

I have 45 dropdown lists in my asp page. There are some methods that I can apply to all of these dropdowns. Is it possible to convert them into an array of dropdowns for ease of use?
I would use recursion to look for all dropdowns on your page. Based on this post it would be something like:
public static List<T> FindControls<T>(System.Web.UI.ControlCollection Controls) where T : class
{
List<T> found = new List<T>();
FindControls<T>(Controls, found);
return found;
}
private static void FindControls<T>(System.Web.UI.ControlCollection Controls, List<T> found) where T : class
{
if (Controls != null && Controls.Count > 0)
{
for (int i = 0; i < Controls.Count; i++)
{
if (Controls[i] is T)
{
found.add(Controls[i] as T);
}
else
FindControl<T>(Controls[i].Controls, found);
}
}
}
Once you have your list of dropdowns you can apply whatever methods you see fit.
Using Linq to Objects, an Extension method and Generics we can make this very concise thus:
Method Call to Get all DropDowns
var DropDowns = FindAllControlsByType<DropDownList>(MyBaseControlArray);
Find Controls Method
public static IEnumerable<Control> FindAllControlsByType<T>(IEnumerable<Control> MyCollection) where T : class
{
return MyCollection.Cast<Control>().Descendants(d => d.Controls.Cast<Control>()).Where(l => l.GetType().Equals(typeof(T)));
}
Descendants Extension Method
static public class LinqExtensions
{
static public IEnumerable<T> Descendants<T>(this IEnumerable<T> source,
Func<T, IEnumerable<T>> DescendBy)
{
foreach (T value in source)
{
yield return value;
foreach (T child in DescendBy(value).Descendants<T>(DescendBy))
{
yield return child;
}
}
}
}
EDIT
I've been looking at making this all a mite more generic so here is a completely generic solution that will traverse an object graph from a specified start point extracting all elements of the given type.
public static class Utils
{
public static IEnumerable<IEnumerable<T>> GetCollections<T>(object obj)
{
if (obj == null) throw new ArgumentNullException("obj");
var type = obj.GetType();
var res = new List<IEnumerable<T>>();
foreach (var prop in type.GetProperties())
{
// is IEnumerable<T>?
if (typeof(IEnumerable<T>).IsAssignableFrom(prop.PropertyType))
{
var get = prop.GetGetMethod();
if (!get.IsStatic && get.GetParameters().Length == 0) // skip indexed & static
{
var collection = (IEnumerable<T>)get.Invoke(obj, null);
if (collection != null) res.Add(collection);
}
}
}
return res;
}
public static IEnumerable<V> FindAllControlsByType<V, T>(V MyCollection) where T : class
{
return Utils.GetCollections<V>(MyCollection).Descendants(d => d).Where(l => typeof(T).IsAssignableFrom(l.GetType()));
}
}
static public class LinqExtensions
{
static public IEnumerable<T> Descendants<T>(this IEnumerable<IEnumerable<T>> source,
Func<IEnumerable<IEnumerable<T>>, IEnumerable<IEnumerable<T>>> DescendBy)
{
foreach (IEnumerable<T> collection in source)
{
foreach (T value in collection)
{
yield return value;
foreach (T child in DescendBy(Utils.GetCollections<T>(value)).Descendants<T>(DescendBy))
{
yield return child;
}
}
}
}
}
And we can call that using:
var DropDowns = Utils.FindAllControlsByType<Control, DropDownList>(BaseControl);
Basically the two types are the base class and the specific child class that you want to extract. You'll notice that the process identifies all collections of the base class that are contained within each instance of the base class. This means we're not tied to Controls as the collection and could use this within other structures. Any additional optimisations welcomed.

Unable to hook into PropertyChanged event using MVVM-Light

Greetings, creating my first MVVM based WPF app and trying to figure out why I'm unable to hook into the PropertyChanged event of a dependency property.
Code in the parent view model:
void createClients()
{
var clients = from client in Repository.GetClients()
select new ClientViewModel(Repository, client);
foreach (var client in clients)
{
client.PropertyChanged += onClientPropertyChanged;
}
Clients = new ViewableCollection<ClientViewModel>(clients);
Clients.CollectionChanged += onClientsCollectionChanged;
}
// Never gets called
void onClientPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
{
//...
}
}
ViewableCollection is a simple extension of ObservableCollection to encapsulate a View.
In the ClientViewModel the setters are being called but RaisePropertyChanged isn't working as I would expect, because onClientPropertyChanged isn't being invoked. Both view models inherit from ViewModelBase.
public string Name
{
get { return client.Name; }
set
{
if (value == client.Name) return;
client.Name = value;
RaisePropertyChanged("Name");
}
}
If I wire up PropertyChanged to a method inside the ClientViewModel then it is being fired, so I'm stumped as to why this isn't working in the parent view model. Where am I going wrong?
This SO question explains the problem; ObservableCollection protects the PropertyChanged event.
One solution is to use MVVM-Light Messenger:
void createClients()
{
var clients = from client in Repository.GetClients()
select new ClientViewModel(Repository, client);
Clients = new ViewableCollection<ClientViewModel>(clients);
Clients.CollectionChanged += onClientsCollectionChanged;
Messenger.Default.Register<PropertyChangedMessage<string>>(this, (pcm) =>
{
var clientVM = pcm.Sender as ClientViewModel;
if (clientVM != null && pcm.PropertyName == "Name")
{
// ...
}
});
}
createClients() should be refactored, but for consistency with the question code I'll leave it in there. Then a slight change to the property setter:
public string Name
{
get { return client.Name; }
set
{
if (value == client.Name) return;
string oldValue = client.Name;
client.Name = value;
RaisePropertyChanged<string>("Name", oldValue, value, true);
}
}

Resources