Why is my hit test bringing back way too many items? - c#

I've got a hit test I'm using on a canvas with a bunch of lines loaded from a dxf file and it's returning an unexpectedly large amount of line objects each time even though my ellipse geometry is quite small. See below:
private void map_page_canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var selectedElements = new List<DependencyObject>();
Point pt = e.GetPosition((UIElement)sender);
var rect = new EllipseGeometry(pt, 0.5, 0.5);
var hitTestParams = new GeometryHitTestParameters(rect);
var resultCallback = new HitTestResultCallback(
result => HitTestResultBehavior.Continue);
var filterCallback = new HitTestFilterCallback(
element =>
{
if (VisualTreeHelper.GetParent(element) == map_page_canvas)
{
selectedElements.Add(element);
}
return HitTestFilterBehavior.Continue;
});
VisualTreeHelper.HitTest(
map_page_canvas, filterCallback, resultCallback, hitTestParams);
foreach (var element in selectedElements)
{
Console.WriteLine(element.GetType());
}
}
Here's the canvas setup:
<Canvas Background="Transparent"
Grid.Column="1" Grid.Row="1"
ClipToBounds="True"
SizeChanged="ViewportSizeChanged"
MouseLeftButtonDown="ViewportMouseLeftButtonDown"
MouseLeftButtonUp="ViewportMouseLeftButtonUp"
MouseMove="ViewportMouseMove"
MouseWheel="ViewportMouseWheel">
<Canvas x:Name="map_page_canvas" Width="800" Height="500" ClipToBounds="False"
Grid.Column="1" Grid.Row="1" HorizontalAlignment="Center"
VerticalAlignment="Center" Background="White" MouseWheel="ViewportMouseWheel" MouseLeftButtonDown="map_page_canvas_MouseLeftButtonDown"
>
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="-1" CenterX="1" CenterY="1" />
</Canvas.LayoutTransform>
<Canvas.RenderTransform>
<MatrixTransform x:Name="transform"/>
</Canvas.RenderTransform>
</Canvas>
</Canvas>
<Border BorderThickness="0.5 0.5 0 0.5" BorderBrush="Black"
Grid.Column="1" Grid.Row="1"/>

Adding elements must be done in the HitTestResultCallback, not in the HitTestFilterCallback. In fact, you should not use a HitTestFilterCallback at all.
var resultCallback = new HitTestResultCallback(
result =>
{
if (VisualTreeHelper.GetParent(result.VisualHit) == map_page_canvas)
{
hitList.Add(result.VisualHit);
}
return HitTestResultBehavior.Continue;
});
...
VisualTreeHelper.HitTest(map_page_canvas, null, resultCallback, hitTestParams);

For whatever reason, replacing my filterCallback variable with a method fixed my issue. See below method:
private HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
if (VisualTreeHelper.GetParent(result.VisualHit) == map_page_canvas)
{
hitList.Add(result.VisualHit);
}
return HitTestResultBehavior.Continue;
}

Related

Why do the TextBlocks in my Canvas not display?

I was intending to overlay multiple Canvas, between 4 and 6 layers, on top of a large Image, in order that I can set all objects in a given Canvas as visible or invisable with the simplicity of a show or Hide routine in a layer class. UpdateLayers simply has a set of calls to each layer.Update(). In the case of the settlementNames layer, it would seem that the Update code is not doing its job. It is supposed work like this;
private void ShowCities_Click(object sender, RoutedEventArgs e)
{
UpdateLayers();
settlements.Show(Settlements);
settlementNames.Show(SettlementNames);
}
public void Show(Canvas canvas)
{
canvas.Visibility = Visibility.Visible;
}
This worked perfectly with the first canvas containing icon sized BitmapImages at ZIndex 1 (the large Image is essentially the background with ZIndex 0). When I tried to add a further canvas at ZIndex 2, the code steps through as expected but does not show the contents. This time the contents is a set of TextBlocks.
The AssociatedCanvas property in the code, has been checked and is the correct Canvas instance, which was laid down in the XAML main window.
public void Update(string layerSelectSqlQuery, LayerType layerType)
{
DataTable layerDataTable = null;
int x = -1;
int y = -1;
string label;
using (MySqlClientWrapper db = new MySqlClientWrapper("Server = localhost; Database = tribes;Uid = root;Pwd = xxxxxxxxx;"))
{
// TODO add population column - and filter to those settlements considered cities.
layerDataTable = db.GetDataTable(layerSelectSqlQuery);
}
AssociatedCanvas.Children.Clear();
foreach (DataRow dataRow in layerDataTable.Rows)
{
x = (int)dataRow["MapX"];
y = (int)dataRow["MapY"];
label = dataRow["Name"].ToString();
if (x != -1 && y != -1)
{
switch (layerType)
{
case LayerType.Settlements:
DrawBitmapImage(x, y);
break;
case LayerType.SettlementNames:
WriteLabel(x, y, label, Color.FromRgb(0, 0, 0));
break;
case LayerType.Units:
break;
case LayerType.UnitNames:
break;
default:
break;
}
}
}
}
Public void WriteLabel(int x, int y, string text, Color color)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = text;
textBlock.Foreground = new SolidColorBrush(color);
Canvas.SetLeft(textBlock, x);
Canvas.SetTop(textBlock, y);
AssociatedCanvas.Children.Add(textBlock);
}
The XAML looks like this in part:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!--<Slider Grid.Column="0" Orientation="Vertical" HorizontalAlignment="Left" Minimum="1" x:Name="slider" />-->
<ScrollViewer Name="mapScroller" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid Name="grid" RenderTransformOrigin="0.5,0.5">
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleTransform" />
</TransformGroup>
</Grid.LayoutTransform>
<Viewbox Grid.Column="0" Grid.Row="0" >
<Image x:Name="MainMap" UseLayoutRounding="True" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center"
MouseLeftButtonUp="MainMap_MouseLeftButtonUp" Source="{Binding MainTerrainMap}"></Image>
</Viewbox>
<Canvas x:Name="Settlements" Panel.ZIndex="1" />
<Canvas x:Name="SettlementNames" Panel.ZIndex="2" >
</Canvas>
</Grid>
</ScrollViewer>
</Grid>

Trying to keep five set of image(png format) in continous animation

I'm trying to keep five set of image(png format) in continuous animation which should occupy full screen and after clicking on that popup it should disappear.
There is same image with different position its feel like it is moving up and down continously. In the below code I have used only one image "good_job.png" as popup .But how to use five different image to show in motion up and down.Any help would be appreciated.
Xaml
<RelativePanel x:Name="contentPanel" Grid.Row="1">
<Canvas RelativePanel.AlignTopWithPanel="True">
<Popup x:Name ="ppup" IsOpen = "False" IsLightDismissEnabled = "True"
Width="420" VerticalAlignment="Top"
>
<Image Source = "Assets/good_job.png" Canvas.ZIndex="1" Width="420" />
</Popup>
<Popup x:Name ="ppup1" IsOpen = "False" IsLightDismissEnabled = "True"
Width="320" VerticalAlignment="Center">
<Image Source = "Assets/wrong_ans.png" Canvas.ZIndex="1" Width="420" />
</Popup>
</Canvas>
</RelativePanel>
<Image x:Name="image1" Source="Assets/LearnColor/Object/ob_0_0.png" Height="150" Width="160" RelativePanel.AlignLeftWithPanel="True" Margin="30,40,0,0" Tapped="image1Tap" d:LayoutOverrides="Width, LeftMargin, RightMargin" />
C# Code
if ((image1.Source as BitmapImage).UriSource == new Uri("ms-appx:///Assets/LearnColor/Object/ob_0_0.png", UriKind.Absolute) && (objNameWritten1.Text == "Banana"))
{
ppup.Height = Window.Current.Bounds.Height;
ppup.IsOpen = true;
mediaElement1.Source = new Uri("ms-appx:///Audio/wow good job.mp3");
mediaElement1.AutoPlay = true;
}
Note: This answers is untested and quickly thrown together. I submitted it because I was asked in the comments.
First create a way to loop through the images.
private int CurrentImageId = 0;
private List<string> Images = new List<string>() { "image1", "image2", "image3", "image4", "image5" };
private string NextImage() {
CurrentImageId = (CurrentImageId + 1) % 5;
return Images[CurrentImageId];
}
Then use a timer to reasign the ImageSource at an interval.
var timer = new Timer(100);
timer.Elapsed += (sender, args) => Images.ImageSource = NextImage();
timer.Start();

How implement an infinity list in a combobox with int values

I want to implement a dynamic combobox what contain int values and its dynamically load new elements before the user is reaching the end of the list.
So for example I load the list with the initial value of 15 and the list initially contains +- 5 element and when user reach the +- 3. element from the end of the list then its add five more element respectively to the end or the beginning.
How can I archive this?
To do that, you need to handler the ScrollChanged event of the scrollViwer control inside the Combobox's ControlTemplate.
First, edit the combobox Template: from visual studio designer selecte > Edit template > Edit a Copy,
In the generated style search for DropDownScrollViewer and add a ScrollChanged event handler
XAML
//..
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer" ScrollChanged="OnScrollChanged">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
//..
Now, let say your combobox is bound to an ObservableCollection like this :
<ComboBox x:Name="CbBox" ItemsSource="{Binding CboxItems}" VerticalAlignment="Center" HorizontalAlignment="Center" Style="{DynamicResource ComboBoxStyle1}"/>
ViewModel
private ObservableCollection<string> _cboxItemsCollection = new ObservableCollection<string>()
{
"0",
"1",
"2",
"3",
"4",
"5",
};
public ObservableCollection<string> CboxItems
{
get
{
return _cboxItemsCollection;
}
set
{
if (_cboxItemsCollection == value)
{
return;
}
_cboxItemsCollection = value;
OnPropertyChanged();
}
}
Handler
the handler should look like that:
private void OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
{
for (int i = 0; i < 5; i++)
{
CboxItems.Add((CboxItems.Count+i).ToString());
}
}
}
EDIT
i forgot to mention about the scroll to top, following the same principle when the VerticalOffset of the scrollViewer is equal to 0, insert the negative items at the top of the collection:
//..
if (scrollViewer.VerticalOffset == 0)
{
for (int i = 0; i < 5; i++)
{
CboxItems.Insert(0,"negaive item");
}
scrollViewer.ScrollToVerticalOffset(10);
}
//..
the scrollViewer.ScrollToVerticalOffset(10); is to prevent the infinite loop since by default the scrollViewer always scroll to the top.

Accessing XAML elements from C# using string or arrays

I am new to WindowsApp Development and I was trying to access XAML elements through C# code using an array.
For example I have few ellipses in my XAML code-
<Ellipse Fill="#FFF4F4F5" x:Name="E_0" Grid.Row="0" Grid.Column="2" Stroke="Black" RenderTransformOrigin="0.474,5.849" Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Ellipse Fill="#FFF4F4F5" x:Name="E_1" Grid.Row="0" Grid.Column="3" Stroke="Black" RenderTransformOrigin="0.474,5.849" Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Ellipse Fill="#FFF4F4F5" x:Name="E_2" Grid.Row="0" Grid.Column="4" Stroke="Black" RenderTransformOrigin="0.474,5.849" Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center"/>
Now I want to work with them in C# in a loop so I am doing something similar to the following-
string s1="E_";
double width=500;
for (int i = 0; i < 2;i++ )
{
string name_i = s1 + i.ToString();
name_i.Width = width / 2;
}
But name_i.width gives me an error. So, do I have to use the actual names, is there no way I can use an array or string? Using the actual names will defeat the purpose as I have about 50 such elements which I need to work upon.
As Maximillian proposed, you can use the parent container and iterate over its children:
Xaml:
<StackPanel Name="StackPanelContainer">
<Grid></Grid>
<Ellipse Name="E_0"></Ellipse>
<Ellipse Name="E_1"></Ellipse>
<Ellipse Name="E_2"></Ellipse>
</StackPanel>
Codebehind:
//if you are sure every child element is a ellipse, you can use:
foreach (Ellipse child in StackPanelContainer.Children)
{
child.Width = 100;
}
//if there are also other elements, and also check if name starts with "E_"
foreach (object child in StackPanelContainer.Children)
{
var ellipse = child as Ellipse;
if (ellipse != null && ellipse.Name.StartsWith("E_"))
{
ellipse.Width = 100;
}
}
Use FindName() method.
string s1 = "E_";
double width = 500;
for (int i = 0; i < 2; i++)
{
string name_i = s1 + i.ToString();
var ellipse = FindName(name_i) as Ellipse;
if (ellipse != null)
{
ellipse.Width = width / 2;
}
}

Telerik RadJumpList using DataVirtualizationMode.Automatic

I have a problem where im trying to use a Telerik Jump List with DataVirtualizationMode.Automatic, but i can't get it to work. The reason why i want to use this, is because i want my app to only download the data(games) which is in the current view of the Jump List control and not the whole data everytime. For example if i have searched for "Batman", and its returning 50 games, i don't want it to download and load all the games, only those i can see in the Jump List control.
Here is a sample of using DataVirtualizationMode.Automatic from Telerik, but i couldn't get it to work with my app: http://www.telerik.com/help/windows-phone/raddataboundlistbox-features-datavirtualization-automatic.html
Below is my Jump List control which i want to use with data virtualization.
MainPage.xaml:
<phone:PivotItem Header="Browse">
<Grid>
<telerikPrimitives:RadTextBox Name="txtSearch" HorizontalAlignment="Left" VerticalAlignment="Top" Height="80" Width="390"/>
<telerikPrimitives:RadImageButton Name="imgBtnSeachGame" VerticalAlignment="Top" HorizontalAlignment="Right" ButtonShape="Ellipse" BorderThickness="2" Margin="0,8,0,0" Click="imgBtnSeachGame_Click"></telerikPrimitives:RadImageButton>
<Controls:RadJumpList Name="jlGameList" ItemsSource="{Binding}" Tap="jlGameList_Tap" Margin="0,90,0,0" DataVirtualizationMode="Automatic">
<Controls:RadJumpList.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="{StaticResource PhoneAccentBrush}"
Padding="{StaticResource PhoneTouchTargetOverhang}"
Margin="0,0,0,0">
<TextBlock Name="tblGameTitle" Style="{StaticResource PhoneTextGroupHeaderStyle}" ManipulationStarted="tblGameTitle_ManipulationStarted" ManipulationCompleted="tblGameTitle_ManipulationCompleted">
<Run Text="{Binding GameTitle}"></Run>
</TextBlock>
</Border>
<Grid Background="#242424" Grid.Row="1">
<Image Name="imgGameList" Margin="0,0,0,0" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top" Height="96" Width="96">
<Image.Source>
<BitmapImage UriSource="{Binding BoxArtFrontThumb}"
CreateOptions="BackgroundCreation" DecodePixelHeight="96" DecodePixelWidth="96" />
</Image.Source>
</Image>
<TextBlock Margin="110,0,0,0" Text="Platform" FontWeight="Bold" TextWrapping="Wrap" Foreground="YellowGreen" FontSize="{StaticResource PhoneFontSizeNormal}"/>
<TextBlock Name="txtPlatform" Margin="110,20,0,0" Text="{Binding Platform}"></TextBlock>
<TextBlock Text="Release Date" FontWeight="Bold" Margin="110,46,0,0" Foreground="YellowGreen" FontSize="{StaticResource PhoneFontSizeNormal}"/>
<TextBlock Name="txtReleaseDate" Margin="110,66,0,0" Text="{Binding ReleaseDate}"></TextBlock>
<!--</StackPanel>-->
</Grid>
<Grid Grid.Row="2"></Grid>
</Grid>
</DataTemplate>
</Controls:RadJumpList.ItemTemplate>
</Controls:RadJumpList>
</Grid>
</phone:PivotItem>
Below is where i bind my DataContext to my GetGamesListItems ObservableCollection in my GameData class. The imgBtnSearchGame_Click event method is being called when a user have typed for example "Batman" in my textbox txtSearch and tapped the button, it will then send the text to my GetGamesListData method.
MainPage.cs:
GameData gd = new GameData();
public MainPage()
{
InitializeComponent();
jlGameList.DataContext = gd.GetGamesListItems;
}
private void imgBtnSeachGame_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(txtSearch.Text))
{
gd.GetGamesListData(txtSearch.Text, "", "");
}
}
Below is where i download the data in XML for the game name searched for. For example if it is "Batman" it will find and return all games with "Batman". The "BoxArtFrontThumb" Property is where im storing all the images for each game and is using async, because sometimes there can be quite alot of images it has to download and show.
GameData.cs
public void GetGamesListData(string name, string platform, string genre)
{
var webClient = new WebClient();
webClient.DownloadStringCompleted += GetGamesListRequestCompleted;
webClient.DownloadStringAsync(new Uri("http://thegamesdb.net/api/GetGamesList.php?name=" + name));
}
private async void GetGamesListRequestCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
GetGamesListItems.Clear();
var feedXml = XDocument.Parse(e.Result);
var gameDataTasks = feedXml.Root.Descendants("Game").Select(
async x => new GetGamesList
{
ID = (int)x.Element("id"),
GameTitle = (string)x.Element("GameTitle"),
ReleaseDate = (string)x.Element("ReleaseDate") ?? "N/A",
Platform = (string)x.Element("Platform") ?? "N/A",
BoxArtFrontThumb = new Uri(await GetBoxArtFrontThumbAsync((int)x.Element("id")), UriKind.RelativeOrAbsolute),
}).ToList();
var gameData = await Task.WhenAll(gameDataTasks);
foreach (var item in gameData)
{
GetGamesListItems.Add(item);
}
}
}
Below is where its finding and storing the images for the games.
public async Task<string> GetBoxArtFrontThumbAsync(int id)
{
var client = new HttpClient();
var result = await client.GetStringAsync("http://thegamesdb.net/api/GetArt.php?id=" + id);
var feedXml = XDocument.Parse(result);
var gameData = feedXml.Root.Descendants("Images").Select(x => new GetArt
{
BoxArtFrontThumb = new Uri(GetBoxArtFrontThumb(x), UriKind.RelativeOrAbsolute),
}).ToList();
return gameData.Single().BoxArtFrontThumb.ToString();
}
private static string GetBoxArtFrontThumb(XElement gameNode)
{
string data = "http://thegamesdb.net/banners/" + (string)gameNode.Descendants("boxart")
.FirstOrDefault(b => (string)b.Attribute("side") == "front");
if (data == "http://thegamesdb.net/banners/")
{
data = "/NoImage.jpg";
}
return data;
}
I really hope i explained this well enough and hope that there is someone that can help me solve this problem. Thanks.
Although you are using JumpList, the mechanism for Virtualizing the data is the same as the DataBoundListBox. (You can find more information here in the DataBoundListBox docs. There is a good tutorial using an OData service.)
In order for the Automatic mode to work properly, you need to be using Telerik's VirtualizingDataCollection object and initialize it with the proper arguments (count and page size).
I don't see this in the code you have provided above, can you please open a support ticket so that I can investigate further? See my comment above for the link. Let me know the ticket number and I'll provide further assistance.

Categories