Flexible loop through variables - c#

I'm struggling using XAML variables in a loop, my problem is detailed below :
I've a foreach() loop, I've 4 images in XAML called let's say image1, image2, image3, image4 now I would like to associate a image that I've got its path in my foreach loop to each image variable.
An obvious solution would be :
foreach() {
//my stuff
image1.Source = bitmapSource;
image2.Source = bitmapSource;
image3.Source = bitmapSource;
image4.Source = bitmapSource;
}
But this solution is not flexible, I thought about putting my image variables in an array but I don't think that is possible (at least I haven't found anything that goes in this way).
What's the best / cleanest way to do it ? Thanks

Use LINQ and Enumerable.OfType<TResult> Method:
Grid1.Children.OfType<Image>().ToList().ForEach(c => c.Source = bitmapSource);
I've presumed the images are inside the Grid container with name Grid1. Change accordingly if they are inside another container.
EDIT:
If your images are inside different containers as you pointed out in your last comment you can do this:
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj)
where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
And then:
FindVisualChildren<Image>(MainGrid).ToList().ForEach(c => c.Source = bitmapSource);
Again I've presumed your different containers are inside a main Grid with name MainGrid.

Related

How to save form data to XML or CSV?

We have currently built a application that uses both textbox and combo boxes.
We want to create a save button in a User Control Library that we have already built, and have it save all the data currently entered into an XML or CSV. The easier of the two would be preferred however I feel they're most likely the same difficulty.
I am using WPF! I would like to make it so that the data is stored to a file. Nothing amazing needs to happen to the file, it's just a case of saving the data from the different textboxes, and comboBoxes and then just having them stored within a document.
Okay, here's something to try. First, because you're using WPF, you need a way to get the controls of a specific type. Here is an extension method for you:
public static class DependencyObjectExtensions
{
public static IEnumerable<T> GetChildren<T>(this DependencyObject parent) where T : DependencyObject
{
List<T> childControls = new List<T>();
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
DependencyObject childControl = VisualTreeHelper.GetChild(parent, i);
if (childControl is T)
{
childControls.Add((T)childControl);
}
}
return childControls;
}
}
Next, you need a method to create your CSV:
public string ToCsv(params IEnumerable<Control>[] controls)
{
return ToCsv(true, controls);
}
public string ToCsv(bool outputColumnNames, params IEnumerable<Control>[] controls)
{
var sb = new StringBuilder();
if (outputColumnNames)
{
foreach (var textBox in controls.OfType<TextBox>())
{
sb.Append(textBox.Name);
sb.Append(",");
}
foreach (var comboBox in controls.OfType<ComboBox>())
{
sb.Append(comboBox.Name);
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1);
}
sb.AppendLine();
foreach (var textBox in controls.OfType<TextBox>())
{
sb.Append(textBox.Text);
sb.Append(",");
}
foreach (var comboBox in controls.OfType<ComboBox>())
{
sb.Append(comboBox.Text);
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
and lastly... use it like this:
var textBoxes = grid.GetChildren<TextBox>();
var comboBoxes = grid.GetChildren<ComboBox>();
string csv = ToCsv(textBoxes, comboBoxes);
In the above, grid is defined on my test form like this:
<Grid Name="grid">
and the controls are children of that. Hope this helps you.
That's a pretty broad question, but the general outline is that you would have to serialize the data into whichever format you end up going with (which would depend on what you plan to do with the data later: Are you sending it to another customer who needs it in one format or the other? Are you just saving the data to have it on record?) Also, bear in mind that CSV is a flat file, i.e. each line of the file is a row and each comma-separated value is in a column, whereas XML is a hierarchical format, i.e. a tree, like HTML. This makes CSV better for storing things like database rows and makes XML better for storing hierarchical things, like an object graph, a document layout, etc. So it really depends on what you're trying to do (and you might want to add more detail to your question in order to get better answers).
private void btnSave_Click(object sender, EventArgs e)
{
var lines = new List<string>();
foreach (Control c in this.Controls)
{
if (c is TextBox)
lines.Add(string.Format("{0},{1}", ((TextBox)c).Name, ((TextBox)c).Text));
if (c is ComboBox)
lines.Add(string.Format("{0},{1}", ((ComboBox)c).Name, ((ComboBox)c).Text));
}
System.IO.File.WriteAllLines(#"data.csv", lines);
}
For WPF:
A helper:
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
The code:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var lines = new List<string>();
foreach (TextBox tb in WindowHelper.FindVisualChildren<TextBox>(this))
lines.Add(string.Format("{0},{1}", tb.Name, tb.Text));
foreach (ComboBox cb in WindowHelper.FindVisualChildren<ComboBox>(this))
lines.Add(string.Format("{0},{1}", cb.Name, cb.Text));
System.IO.File.WriteAllLines(#"data.csv", lines);
}

How to set a clr-namespace property programatically in WPF

I am new with WPF, so I'm not sure if the title of the question is correct or makes any sense, please edit if it can get more relevant. I am using Kinect.Toolbox MouseControl in my application. For using the magnetic controls I have a problem. I know that I can define them in XAML by adding:
<Page ...
xmlns:local ="clr-namespace:Kinect.Toolbox;assembly=Kinect.Toolbox">
...
<Button local:MagneticPropertyHolder.IsMagnetic="True" ... />
....
But I need to do it in the code. Is there anyway to set the magnetic controls in the code? I can get all the controlls in the page like this:
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
foreach (Button tb in FindVisualChildren<Button>(this))
{
//Set the buttons to be magnetic
}
However I cannot understand how to set them progmatically.
This looks like an attached property.
To set it, you'd do something like
tb.SetValue(MagneticPropertyHolder.IsMagneticProperty, true);
or possibly
MagneticPropertyHolder.SetIsMagnetic(tb, true);
A quick glance at the Kinect Toolbox source code suggests that either would work. The second is more type safe.
See How to I access an attached property in code behind? for more information.

Get ListView Visible items

I have a ListView which might contains a lot of items, so it is virtualized and recycling items. It does not use sort. I need to refresh some value display, but when there are too many items, it is too slow to update everything, so I would like to refresh only the visible items.
How could I get a list of all currently displayed items ? I tried to look into the ListView or in the ScrollViewer, but I still have no idea how to achieve this. The solution must NOT go through all items to test if they can be seen, because this would be too slow.
I'm not sure code or xaml would be useful, it is just a Virtualized/Recycling ListView with its ItemSource bound to an Array.
Edit :
Answer :
thanks to akjoshi, I found the way :
get the ScrollViewer of the ListView
(with a FindDescendant method, that you can do yourself with the VisualTreeHelper ).
read its ScrollViewer.VerticalOffset : it is the number of the first item shown
read its ScrollViewer.ViewportHeight : it is the count of items shown.
Rq : CanContentScroll must be true.
Have a look at this question on MSDN showing a technique to find out the visible ListView items -
How to find the rows (ListViewItem(s)) in a ListView that are actually visible?
Here's the relevant code from that post -
listView.ItemsSource = from i in Enumerable.Range(0, 100) select "Item" + i.ToString();
listView.Loaded += (sender, e) =>
{
ScrollViewer scrollViewer = listView.GetVisualChild<ScrollViewer>(); //Extension method
if (scrollViewer != null)
{
ScrollBar scrollBar = scrollViewer.Template.FindName("PART_VerticalScrollBar", scrollViewer) as ScrollBar;
if (scrollBar != null)
{
scrollBar.ValueChanged += delegate
{
//VerticalOffset and ViweportHeight is actually what you want if UI virtualization is turned on.
Console.WriteLine("Visible Item Start Index:{0}", scrollViewer.VerticalOffset);
Console.WriteLine("Visible Item Count:{0}", scrollViewer.ViewportHeight);
};
}
}
};
Another thing you should do is to use ObservableCollection as your ItemSource instead of an Array; that will definitely improve the performance.
Update:
Ya that might be true(array vs. ObservableCollection) but I would like to see some statistics related to this;
The real benefit of ObservableCollection is if you have a requirement to add/remove items from your ListView at run-time, in case of an Array you will have to reassign the ItemSource of ListView and the ListView first throws away its previous items and regenerates its entire list.
After trying to figure out something similar, I thought I would share my result here (as it seems easier than the other responses):
Simple visibility test I got from here.
private static bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
if (!element.IsVisible)
return false;
Rect bounds =
element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
var rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}
Afterwards you can loop through the listboxitems and use that test to determine which are visible. Since the listboxitems are always ordered the same the first visible one in this list would be the first visible one to the user.
private List<object> GetVisibleItemsFromListbox(ListBox listBox, FrameworkElement parentToTestVisibility)
{
var items = new List<object>();
foreach (var item in PhotosListBox.Items)
{
if (IsUserVisible((ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item), parentToTestVisibility))
{
items.Add(item);
}
else if (items.Any())
{
break;
}
}
return items;
}
How I see things :
on one side, you have your data. They must be up to date, because this is where your information is in memory. Iterating on your data list should be pretty fast, and most of all, can be done on another thread, in background
on the other side, you have the display. Your ListView already make the trick of refreshing only the datas displayed, since it's virtualizing ! You need no more tricks, it's already in place !
On last work, using a binding on an ObservableCollection is a good advice. If you intend to modify the ObservableCollection from an another thread, I would recommend this : http://blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection-binding-part-1/
I spend a lot of time finding a better solution for this,
In my situation i have a scrollviewer, filled with items with custom heigths that can be set visible/invisible, i came up with this. It does the same as above solutions but with a fraction of the CPU. I hope it helps some one.
The first items of the listview / scrollpanel is TopVisibleItem
public int TopVisibleItem { get; private set; }
private double CurrentDistance;
private void TouchScroller_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (myItemControl.Items.Count > 0)
{
MoveDirection direction = (MoveDirection)Math.Sign(e.VerticalChange);
if (direction == MoveDirection.Positive)
while (CurrentDistance < e.VerticalOffset && TopVisibleItem < myItemControl.Items.Count)
{
CurrentDistance += ((FrameworkElement)myItemControl.Items[TopVisibleItem]).ActualHeight;
TopVisibleItem += 1;
}
else
while (CurrentDistance >= e.VerticalOffset && TopVisibleItem > 0)
{
CurrentDistance -= ((FrameworkElement)myItemControl.Items[TopVisibleItem]).ActualHeight;
TopVisibleItem -= 1;
}
}
}
public enum MoveDirection
{
Negative = -1,
Positive = 1,
}
If you have a virtualization enabled ListView, Then you can get all Current Visible items as below:
Get VirtualizingStackPanel
Get all ListViewItems in VirtualizingStackPanel
The code is shown below.
VirtualizingStackPanel virtualizingStackPanel = FindVisualChild<VirtualizingStackPanel>(requiredListView);
List<ListViewItem> items = GetVisualChildren<ListViewItem>(virtualizingStackPanel);
The Functions are shown below.
private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
private List<childItem> GetVisualChildren<childItem>(DependencyObject obj) where childItem : DependencyObject
{
List<childItem> childList = new List<childItem>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
childList.Add(child as childItem);
}
if (childList.Count > 0)
return childList;
return null;
}
This will return you list of current ListViewItem loaded for displaying.
Hope it helps :).

Accessing UIElement after XAML Load in WPF

I have a Canvas being populated from a XAML load from a file using:
SBWindowContainerCanvas.Children.Clear();
StreamReader stringreader = new StreamReader("C:\\xaml\\xmltest.xaml");
XmlReader xmlreader = XmlReader.Create(stringreader);
var mainborder = XamlReader.Load(xmlreader);
SBWindowContainerCanvas.Children.Add((Blacklight.Controls.ClippingBorder)mainborder);
The border contains many child elements including some user controls.
partial snippet of XAML file:
<av:ContentControl Name="VisScoreCC" Width="417" Height="228" IsHitTestVisible="True" av:Canvas.Left="855" av:Canvas.Top="8" av:Selector.IsSelected="False">
<fsp:Scores3DigitControl Name="Vis3DigitScoreControl" VisitorControl="True" OperatorControl="True" NumericValue="4" UseGradientMainBG="False" UseGradientNumberBG="False" UseGradientLogoBG="False" UseGradientTeamNameBG="False" UseRadialMainBG="False" UseRadialNumberBG="False" UseRadialLogoBG="False" UseRadialTeamNameBG="False" UseImageStringMainBG="False|none|False|Tile|Top Left|1" SolidColorMainBG="#FFD4D2D2" SolidColorNumberBG="#FF000000" SolidColorBannerBG="#FF000000" FGColorNumbers="#FFFF0000" FGColorTeamName="#FFFFFFFF" FGColorBanner="#FFFFA500" SolidColorImageLogoBG="#FF000000" SolidColorTeamNameBG="#FF000000" GradientMainBG="#FF000000|#FF808080|#FFFFFFFF" GradientNumberBG="#FF000000|#FF808080|#FFFFFFFF" GradientLogoBG="#FF000000|#FF808080|#FFFFFFFF" GradientTeamNameBG="#FF000000|#FF808080|#FFFFFFFF" GradientOffsetsMainBG="0|0.5|1" GradientOffsetsNumbersBG="0|0.5|1" GradientOffsetsLogoBG="0|0.5|1" GradientOffsetsTeamNameBG="0|0.5|1" LinearAngleMainBG="180" LinearAngleNumberBG="180" LinearAngleLogoBG="180" LinearAngleTeamNameBG="180" OffSegmentOpacity="0.1" RoundRadiusNumbers="0" RoundRadiusLogo="0" RoundRadiusTeamName="0" PosSizeStringNumbers="180|8|200|150" PosSizeStringLogo="20|10|150|150" PosSizeStringTeamName="25|165|360|60" TeamNameFontString="Arial|40|True" UseRightSideBanner="True" Style="{av:DynamicResource Scores3DigitControlTemplate}" Height="Auto" Margin="0,0,0,0" IsHitTestVisible="True" />
</av:ContentControl>
<av:ContentControl Name="HomeScoreCC" Width="417" Height="228" IsHitTestVisible="True" av:Canvas.Left="11" av:Canvas.Top="8" av:Selector.IsSelected="False">
<fsp:Scores3DigitControl Name="Home3DigitScoreControl" VisitorControl="False" OperatorControl="True" NumericValue="4" UseGradientMainBG="False" UseGradientNumberBG="False" UseGradientLogoBG="False" UseGradientTeamNameBG="False" UseRadialMainBG="False" UseRadialNumberBG="False" UseRadialLogoBG="False" UseRadialTeamNameBG="False" UseImageStringMainBG="False|none|False|Tile|Top Left|1" TargetElementIndex="0" SolidColorMainBG="#FFD4D2D2" SolidColorNumberBG="#FF000000" SolidColorBannerBG="#FF000000" FGColorNumbers="#FFFF0000" FGColorTeamName="#FFFFFFFF" FGColorBanner="#FFFFA500" SolidColorImageLogoBG="#FF000000" SolidColorTeamNameBG="#FF000000" GradientMainBG="#FF000000|#FF808080|#FFFFFFFF" GradientNumberBG="#FF000000|#FF808080|#FFFFFFFF" GradientLogoBG="#FF000000|#FF808080|#FFFFFFFF" GradientTeamNameBG="#FF000000|#FF808080|#FFFFFFFF" GradientOffsetsMainBG="0|0.5|1" GradientOffsetsNumbersBG="0|0.5|1" GradientOffsetsLogoBG="0|0.5|1" GradientOffsetsTeamNameBG="0|0.5|1" LinearAngleMainBG="180" LinearAngleNumberBG="180" LinearAngleLogoBG="180" LinearAngleTeamNameBG="180" OffSegmentOpacity="0.1" RoundRadiusNumbers="0" RoundRadiusLogo="0" RoundRadiusTeamName="0" PosSizeStringNumbers="35|8|200|150" PosSizeStringLogo="250|10|150|150" PosSizeStringTeamName="25|165|360|60" TeamNameFontString="Arial|40|True" UseRightSideBanner="False" Style="{av:DynamicResource Scores3DigitControlTemplate}" OverridesDefaultStyle="False" Name="Home3DigitScoreControl" Height="Auto" IsHitTestVisible="True" />
</av:ContentControl>
When I want to get access to my controls in code-behind, I am not able to actually re-hook to the elements using:
Scores3DigitControl Vis3DigitScoreControlC = (Scores3DigitControl)SBWindowContainerCanvas.FindName("Vis3DigitScoreControl");
if (Vis3DigitScoreControlC == null)
{
MessageBox.Show("Couldn't Find Vis");
}
else
{
Vis3DigitScoreControlC.Visibility = Visibility.Hidden;
}
The UI Elements show up fine on the screen but I always get a "Couldn't Find Vis" message. From everything I read so far, using the FindName should work as long as my element is a child somewhere in the canvas, right? What am I missing?
Thank you
FindName is generally used for getting elements from a template. In your case your probably best to use the LogicalTreeHelper
LogicalTreeHelper.FindLogicalNode(SBWindowContainerCanvas,
"Vis3DigitScoreControl");
Try to use that method:
public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
It should return to you all object which you need. And after you will be able choose one in foreach loop.
Try using x:Name instead of Name and checking after that element has loaded.
LogicalTreeHelper.FindLogicalNode(...) works here, because it goes across NameScope boundries.
Whereas FindName(...) works in same NameScope.
Dynamically adding controls require good understanding of NameScopes.

Compare controls in wpf

sorry about not making myself clear enough and not putting enough effort (wont happen again :)). i'm building a form App where users have to fill out the form. i have a TabControl with 3 TabItem one of the TabItem has TextBoxes the second has TextBoxes and RadioButton and third has only CheckBoxes. i have written a code to dictect error by on clicking submit using ValidationRule/ValidationResult and and using GroupBinding (got it from msdn samples). now the problem am having is code to search through the tabs, compare the controls (e.g. controlA,controlB) to know wich one comes before the other and return the tabindex. one of the use i want with this is letting the user jump to the uncompleted TextBox,RadioButton or CheckBoxes in order like starting from the first Tabitem in the TabControl
with this code i could work the tree to locate the controls(code from Philipp Sumi blog but i modified it a little)
private void Button_Click(
object sender,
RoutedEventArgs e)
{
IEnumerator enumerator = FindLogicalChildren(_parentStackPanel).GetEnumerator();
while (enumerator.MoveNext())
MessageBox.Show(enumerator.Current.ToString());
}
private IEnumerable FindLogicalChildren(
DependencyObject depObj)
{
if (depObj != null)
{
foreach (object childObj in LogicalTreeHelper.GetChildren(depObj))
{
DependencyObject child = childObj as DependencyObject;
if (child != null && child is Control)
{
yield return (Control)child;
}
foreach (Control childOfChild in FindLogicalChildren(child))
{
yield return childOfChild;
}
}
}
}
but i dodnt know how continue to get the tabindex of each control in order form as i work down the tree. can any one please help me on this? Thanks
im using this method:
private int Compare(
Control controlA,
Control controlB)
{
DependencyObject commonAncestor = controlA.FindCommonVisualAncestor(controlB);
for (int index = 0; index < VisualTreeHelper.GetChildrenCount(commonAncestor); index++)
{
Visual childVisual = (Visual)VisualTreeHelper.GetChild(commonAncestor, index);
Control control = (Control)childVisual;
control.TabIndex = index;
}
return controlA.TabIndex.CompareTo(controlB.TabIndex);
}
that returns 1,-1 or 0 to compare two controls to find out which one comes before the other. the question is, can anyone tell me a better way of doing this.

Categories