How to update ObservableCollection? - c#

I have folder with images, which I show in my ListBox. ObservableCollection stores that images. In my program users could crop selected image and after cropping save that image. Problem is that after saving the image my ObservableCollection is not updated (although OnPropertyChange event is rise) and shows the same image. Does anybody have the same problem?
EDIT
I have BindingManager where I place my observableCollection.
public class BindingsManager:INotifyPropertyChanged
{
private ObservableCollection<PhotoModel> _photoList;
public ObservableCollection<PhotoModel> PhotoList
{
get {return _photoList;}
set
{
_photoList = value;
OnPropertyChanged(nameof(PhotoList));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
In Xaml file I bind source to PhotoList:
<ListView x:Name="PhotoListView" ItemsSource="{Binding PhotoList, UpdateSourceTrigger=PropertyChanged}">
After double click on image, image will be opened in new window where user can crop it and save to DefaultDestFolder which is tracking by FileSystemWatcher:
private void WatchDestinationFolder()
{
var watcher = new FileSystemWatcher
{
Path = BindingsManager.DefaultsManager.DefaultDestFolder,
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.LastAccess | NotifyFilters.LastWrite,
Filter = "*.*"
};
watcher.Changed += UpdatePhotoList;
watcher.EnableRaisingEvents = true;
}
And on change event i update my ObservableCollection but it doesn't work:
private void UpdatePhotoList()
{
var files = Directory.GetFiles(BindingsManager.DefaultsManager.DefaultDestFolder, "*.*");
BindingsManager.PhotoList = new ObservableCollection<PhotoModel>();
foreach (var file in files)
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
var fileInfo = new FileInfo(file);
BitmapImage img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.UriSource = new Uri(file, UriKind.Absolute);
img.EndInit();
BindingsManager.PhotoList.Add(new PhotoModel()
{
BitmapImage = img,
FullFileName = fileInfo.FullName,
ShortFileName = fileInfo.Name,
FileLastAccessTime = fileInfo.LastAccessTime,
FileSize = fileInfo.Length,
Width = (int)img.Width,
Height = (int)img.Height,
DirectoryName = fileInfo.DirectoryName,
FileCreationTime = fileInfo.CreationTime
});
}));
}
}
EDIT
<ScrollViewer Grid.Column="2">
<ListView x:Name="PhotoListView" BorderThickness="0"
ItemsSource="{Binding PhotoList, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<controls:Tile Style="{StaticResource TileStyle}">
<StackPanel Background="White" Width="190" Height="140" Orientation="Vertical">
<Image Margin="5" Width="180" Height="110" Stretch="Fill" Source="{Binding BitmapImage}"/>
<TextBlock Text="{Binding ShortFileName}" TextAlignment="Center" Height="20" FontStyle="Italic" FontWeight="ExtraLight" Foreground="Black"></TextBlock>
</StackPanel>
</controls:Tile>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="MouseDoubleClick" Handler="ItemMouseDoubleClick"></EventSetter>
<EventSetter Event="PreviewMouseLeftButtonUp" Handler="ItemMouseClick"></EventSetter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</ScrollViewer>

I think binding isn't your main problem. You should check image caching. Try: Reloading an image in wpf

Related

How to add Listbox Dynamically to Wrap panel at runtime by using Drag and Drop in WPF

This is my code, There is one listbox in right side, the listbox item is added to the another list box item inside the grid. The Grid is added dynamically by using the context menu. Till it is working fine. But now I want to move the whole list not list items, to the grid dynamically at runtime. Please anyone guide me to drag the whole list and placing the whole list to grid at runtime dynamically.
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<WrapPanel
x:Name="mainPanel"
Grid.Column="0"
Background="#F0F0F0"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<WrapPanel.ContextMenu>
<ContextMenu>
<MenuItem Click="MenuItem_Click" Header="Add Pabel" />
</ContextMenu>
</WrapPanel.ContextMenu>
</WrapPanel>
<ListBox
Name="memberCollection"
Grid.Column="1"
Width="150"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
PreviewMouseLeftButtonDown="memberCollection_PreviewMouseLeftButtonDown">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Name="Name" Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
Code Behind
ObservableCollection<Member> member = new ObservableCollection<Member>();
public MainWindow()
{
InitializeComponent();
member.Add(new Member { Name = "Karthick", ID = "20011", Address = "10, MainRoad, Chennai" });
member.Add(new Member { Name = "Suresh", ID = "20012", Address = "11, MainRoad, Madurai" });
member.Add(new Member { Name = "Arun", ID = "20013", Address = "12, MainRoad, Selam" });
member.Add(new Member { Name = "Gokul", ID = "20014", Address = "13, MainRoad, Coimbature" });
member.Add(new Member { Name = "Vishnu", ID = "20015", Address = "14, MainRoad, Goa" });
memberCollection.ItemsSource = member;
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
Grid panel = new Grid();
panel.MinHeight = 150;
panel.MinWidth = 150;
panel.Height = 150;
panel.Width = 150;
panel.Margin = new Thickness(15,15,0,10);
Grid gridDrop = new Grid()
{
Background = Brushes.White,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch
};
gridDrop.Drop += grid_Drop;
var panelTemplate = new DataTemplate();
var stackPanel = new FrameworkElementFactory(typeof(StackPanel));
stackPanel.SetValue(StackPanel.OrientationProperty, Orientation.Vertical);
var name = new FrameworkElementFactory(typeof(Label));
name.SetBinding(Label.ContentProperty, new Binding("Name"));
var id = new FrameworkElementFactory(typeof(Label));
id.SetBinding(Label.ContentProperty, new Binding("ID"));
var address = new FrameworkElementFactory(typeof(Label));
address.SetBinding(Label.ContentProperty, new Binding("Address"));
stackPanel.AppendChild(name);
stackPanel.AppendChild(id);
stackPanel.AppendChild(address);
panelTemplate.VisualTree = stackPanel;
ListBox listBox = new ListBox()
{
AllowDrop = true,
ItemTemplate = panelTemplate
};
gridDrop.Children.Add(listBox);
panel.Children.Add(gridDrop);
mainPanel.Children.Add(panel);
DataContext = new Member();
}
private void memberCollection_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
object selectedMember = memberCollection.SelectedItem as Member;
if (selectedMember != null)
{
DragDrop.DoDragDrop(memberCollection, selectedMember, DragDropEffects.Move);
}
}
private void grid_Drop(object sender, RoutedEventArgs e)
{
ListBox listContent = e.Source as ListBox;
if (listContent != null)
Console.WriteLine("", Grid.GetColumn(listContent), Grid.GetRow(listContent));
DataObject item = (((DragEventArgs)e).Data) as DataObject;
object Target = ((Grid)(sender)).DataContext;
if (Target != null)
{
object listItem = item.GetData(Target.GetType());
listContent.Items.Add(listItem);
}
}
If you only want to move the whole list and create a tile for each Member, then this is one way to go:
First of all, you should simplify your template and put it in the XAML.
XAML is much more efficient at handling any kind of template than the code behind
For your left mainPanel:
<ItemsControl x:Name="mainPanel" Grid.Column="0" ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Background="#F0F0F0" AllowDrop="True" Drop="mainPanel_Drop">
<WrapPanel.ContextMenu>
<ContextMenu>
<MenuItem Click="MenuItem_Click" Header="Add Panel" />
</ContextMenu>
</WrapPanel.ContextMenu>
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Background="White" AllowDrop="True" Margin="15,15,0,10" Width="150" MinWidth="150" Height="150" MinHeight="150">
<Label Content="{Binding Name}"/>
<Label Content="{Binding ID}"/>
<Label Content="{Binding Address}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And for your list (no much changes)
<ListBox
Name="memberCollection"
Grid.Column="1"
Width="150"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding}"
PreviewMouseLeftButtonDown="memberCollection_PreviewMouseLeftButtonDown">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Name="memberName" Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I added two ObservableCollection to manage the Data
ObservableCollection<Member> member = new ObservableCollection<Member>();
ObservableCollection<Member> mainPanelMembers = new ObservableCollection<Member>();
public MainWindow()
{
InitializeComponent();
member.Add(new Member { Name = "Karthick", ID = "20011", Address = "10, MainRoad, Chennai" });
member.Add(new Member { Name = "Suresh", ID = "20012", Address = "11, MainRoad, Madurai" });
member.Add(new Member { Name = "Arun", ID = "20013", Address = "12, MainRoad, Selam" });
member.Add(new Member { Name = "Gokul", ID = "20014", Address = "13, MainRoad, Coimbature" });
member.Add(new Member { Name = "Vishnu", ID = "20015", Address = "14, MainRoad, Goa" });
memberCollection.DataContext = member;
mainPanel.DataContext = mainPanelMembers;
}
Then, the creation of a new Tile is much much more simplier:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
mainPanelMembers.Add(new Member());
}
If you want to copy the whole list, then you have to give the whole ObservableCollection to the DragDrop manager:
private void memberCollection_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
object collectionDataContext = memberCollection.DataContext;
if (collectionDataContext != null)
{
DataObject dragData = new DataObject("DataContext", collectionDataContext);
DragDrop.DoDragDrop(memberCollection, dragData, DragDropEffects.Move);
}
}
And when you drop it, you can simply add the Member one by one. You could also clear the collection before (but I didn't):
private void mainPanel_Drop(object sender, DragEventArgs e)
{
DataObject item = e.Data as DataObject;
object listItem = item.GetData("DataContext") as IEnumerable<Member>;
if (listItem != null)
{
foreach (Member member in (IEnumerable<Member>)listItem)
{
((ObservableCollection<Member>)mainPanel.DataContext).Add(member);
}
}
}
I'm not sure I understood very well what you wanted... I still don't know how you're using the Tile creation.
Let me know if you need to do something different.

Show Mahapps.ProgressRing when loading data in WPF

I'm populating a DataGrid with sorting and grouping in WPF and I want to implement a progress indicator to the user for them to know that the current query is still running in the background. I'm trying to use the ProgressRing of Mahapps but i don't know how do I implement it. Below is my code.
Code Behind
void InitSongs()
{
this.Dispatcher.Invoke((Action)(() => {
DataTable dtSong = new DataTable();
ICollection<Song> songList = new ObservableCollection<Song>();
using (SqlConnection conn = new SqlConnection(#"Server=.\MSSQL2008R2;Database=MVCDB;Trusted_Connection=True;"))
{
string sqlCmd = "SELECT TOP 1000 * FROM SongDb";
conn.Open();
using (SqlCommand cmd = new SqlCommand(sqlCmd, conn))
{
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = sqlCmd;
cmd.CommandTimeout = 120;
dtSong.Load(cmd.ExecuteReader());
}
}
for (int i = 0; i <= dtSong.Rows.Count - 1; i++)
{
songList.Add(new Song(Convert.ToInt32(dtSong.Rows[i][0]),
dtSong.Rows[i][1].ToString(),
dtSong.Rows[i][2].ToString(),
Convert.ToInt64(dtSong.Rows[i][3]),
dtSong.Rows[i][4].ToString(),
Convert.ToBoolean(dtSong.Rows[i][5])));
}
dgSongs.ItemsSource = songList;
ICollectionView view = CollectionViewSource.GetDefaultView(dgSongs.ItemsSource);
view.SortDescriptions.Add(new SortDescription("Artist", ListSortDirection.Ascending));
//view.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
view.GroupDescriptions.Add(new PropertyGroupDescription("Artist"));
}));
}
private void btnGetSongs_Click(object sender, RoutedEventArgs e)
{
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
progress1.IsActive = true;
progress1.Visibility = System.Windows.Visibility.Visible;
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
InitSongs();
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progress1.IsActive = false;
progress1.Visibility = Visibility.Collapsed;
}
MainWindow.xaml
<Controls:MetroWindow x:Class="PalletTaggingWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="Pallet Tagging" Height="500" Width="500" ShowIconOnTitleBar="True"
WindowStartupLocation="CenterScreen"
GlowBrush="{DynamicResource AccentColorBrush}"
BorderBrush="{DynamicResource AccentColorBrush}"
EnableDWMDropShadow="True"
BorderThickness="1">
<TabControl>
<TabItem Header="Songs">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackPanel>
<Button Name="btnGetSongs" Content="Get Songs" Click="btnGetSongs_Click"></Button>
</StackPanel>
<DataGrid Grid.Row="1" Name="dgSongs" ItemsSource="{Binding}">
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=ItemCount}"/>
<TextBlock Text="Items"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
</Grid>
</TabItem>
</TabControl>
PS: I'm using VS2010 for this
XAML
<StackPanel>
<Button Name="btnGetSongs" Content="Get Songs" Click="btnGetSongs_Click"></Button>
<ProgressRing x:Name="progress1"/> <!-- Without MahApps -->
</StackPanel>
CS
this.Dispatcher.Invoke((Action)(() => {
progress1.IsActive = true;
progress1.Visibility = Visibility.Visible;
DataTable dtSong = new DataTable();
//All Steps will go as stated
view.GroupDescriptions.Add(new PropertyGroupDescription("Artist"));
progress1.IsActive = false;
progress1.Visibility = Visibility.Collapsed;
}));
MahApps
XAML
<StackPanel>
<Button Name="btnGetSongs" Content="Get Songs" Click="btnGetSongs_Click"></Button>
<Controls:ProgressRing IsActive="False" Visibility="Collapsed" Name="progress1" />
</StackPanel>
CS
this.Dispatcher.Invoke((Action)(() => {
progress1.IsActive = true;
progress1.Visibility = Visibility.Visible;
DataTable dtSong = new DataTable();
//All Steps will go as stated
view.GroupDescriptions.Add(new PropertyGroupDescription("Artist"));
progress1.IsActive = false;
progress1.Visibility = Visibility.Collapsed;
}));

Infinity scroll bug with data template

After user get the end of longlistselector ItemRealized event fires items add successfully. But the button's tag is empty (for added items), for items that I got first time button's tag isn't null.
This is my xaml DataTemplate:
<DataTemplate x:Key="TitleSongDataTemplate" x:Name="Titledata">
<Border Background="{StaticResource PhoneForegroundBrush}" Margin="0,0,12,12">
<Grid x:Name="GridOfButtons" Background="{StaticResource PhoneForegroundBrush}" >
<Grid Margin="0, -6, -7, 0"
HorizontalAlignment="Right"
VerticalAlignment="Top" Height="81" Width="83">
<Button
Content=""
FontFamily="Segoe UI Symbol"
Style="{StaticResource RoundButton}"
x:Name="AddToMySongsButton"
Tag="{Binding Song}"
Background="{StaticResource PhoneBackgroundBrush}"
Click="AddToMySongsButton_Click"/>
</Grid>
<Grid
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="-17,-6,0,0" Height="81" Width="95">
<Button FontFamily="Segoe UI Symbol"
Content=""
Style="{StaticResource RoundButton}" Margin="10,0,0,0"
x:Name="PlayButton"
Background="{StaticResource PhoneBackgroundBrush}"
Tag="{Binding Song}"
Click="PlayButton_Click"/>
</Grid>
<Grid
HorizontalAlignment="Center"
VerticalAlignment="Top"
Margin="69,-7,60,0" Height="82" Width="84">
<Button Style="{StaticResource RoundButton}"
Content=""
FontFamily="Segoe UI Symbol"
x:Name="DownloadButton"
Margin="-5,0,0,0"
Background="{StaticResource PhoneBackgroundBrush}"
Tag="{Binding Song}"
Click="DownloadButton_Click"/>
</Grid>
<StackPanel VerticalAlignment="Bottom">
<TextBlock Text="{Binding Title}" Foreground="{StaticResource PhoneBackgroundBrush}" Margin="6 , 4, 6, 4"/>
<TextBlock Text="{Binding Artist}" Foreground="Gray" Opacity="0.75" Margin="6, 0, 0, 6"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
My pivot:
<phone:Pivot x:Name="MainTitle">
<phone:PivotItem Header="Rock">
<Grid>
<phone:LongListSelector x:Name="RockLongList"
GridCellSize="220,150"
LayoutMode="Grid"
ItemTemplate="{StaticResource TitleSongDataTemplate}"
ItemRealized="RockLongList_ItemRealized"/>
<TextBlock x:Name="RockText"
Text="Загрузка..."
FontSize="60"
Opacity="0.5"
Style="{StaticResource LongListSelectorGroupHeaderLetterTileStyle}"
Margin="0,10,10,10"
TextWrapping="Wrap"
HorizontalAlignment="Right"
Width="436"/>
</Grid>
</phone:PivotItem>
Here my infinity scrooling realization:
1.I fired ItemRealized
private void RockLongList_ItemRealized(object sender, ItemRealizationEventArgs e)
{
var nownum = offset + 50;
if (nownum >= usercount)
{
MainTitle.Title = "All songs've downloaded";
}
else
{
SpecialSongs.SongJSON song = e.Container.Content as SpecialSongs.SongJSON;
if (song != null)
{
int myoffset = 2;
// Only if there is no data that is currently getting loaded would be initiate the loading again
if (!isCurrentlyLoading && defaultBindList.Count - defaultBindList.IndexOf(song) <= myoffset)
{
LoadDataFromSource();
}
}
}
}
And finally:
private void LoadDataFromSource()
{
this.Dispatcher.BeginInvoke(() =>
{
MainTitle.Title = "Downloading test...";
});
//progressBar.IsVisible = true;
isCurrentlyLoading = true;
string UsersUri = string.Format("https://api.vk.com/method/audio.get?&count=50&access_token={0}&user_id={1}&version=4.92&offset={2}", AccessToken, uID, offset);
//var query = string.Format(datasourceUrl, currentPage);
WebClient client = new WebClient();
client.DownloadStringCompleted += client_DownloadStringCompleted;
client.DownloadStringAsync(new Uri(UsersUri));
}
private void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
//Here handle exception
using (var reader = new MemoryStream(Encoding.Unicode.GetBytes(e.Result)))
{
SpecialSongs songs = JsonConvert.DeserializeObject<SpecialSongs>(e.Result);
this.Dispatcher.BeginInvoke(() =>
{
foreach (var tracks in songs.songs)
{
rockBindList.Add(tracks);
MainTitle.Title = "Downloaded test...";
}
RockLongList.ItemsSource = rockBindList;
isCurrentlyLoading = false;
//progressBar.IsVisible = false;
});
}
offset += 50;
}
This code shows why i need button's tag property:
private void DownloadButton_Click(object sender, RoutedEventArgs e)
{
try
{
SpecialSongs.SongJSON songdgd = (SpecialSongs.SongJSON)(sender as Button).Tag;
this.Download.Message = songdgd.Artist + "\n" + songdgd.Title;
string StringToSave = "shared/transfers/" + songdgd.Artist + songdgd.Title;
var linktosave = StringToSave.Replace(" ", "");
transferRequest = new BackgroundTransferRequest(new Uri(songdgd.URI, UriKind.RelativeOrAbsolute)) { Method = "GET" };
var uri = new Uri(linktosave, UriKind.Relative);
songdgd.URI = linktosave.Replace("shared/transfers/", "");
var json = JsonConvert.SerializeObject(songdgd);
var name = linktosave.Replace("shared/transfers/", "");
transferRequest.Tag = "json" + json + "json" + "uri" + name + "uri";
transferRequest.TransferPreferences = TransferPreferences.AllowCellularAndBattery;
transferRequest.DownloadLocation = uri;
try
{
BackgroundTransferService.Add(transferRequest);
}
catch
{
MessageBox.Show("Эта песня уже загружается.");
}
}
catch { MessageBox.Show("Error while downloading"); }
}
Thanks.

GART Augmented Reality issue

I'm working with windows phone 8 apps and want to add augmented reality feature and I'm using GART, but I experiencing the same issue
in there and even there is a solution by Igor Ralic by adding canvas.zindex to 1, I'm still experiencing the same issue (the items in world view flicker and disappear), so maybe there is anybody in here that having much better solution? I'm using mvvm patern to work with this AR
Here is my approach with mvvm
This is my mainviewmodel
private ObservableCollection<ARItem> _ardisplayLocation = null;
public ObservableCollection<ARItem> ardisplayLocation
{
get { return _ardisplayLocation; }
set { this.SetProperty(ref this._ardisplayLocation, value); }
}
private void UpdateTransport()
{
try
{
myMessage = "Loading web server data...";
WebClient client = new WebClient();
Uri uri = new Uri(transportURL1 + latitude + "%2C" + longitude + transportURL2, UriKind.Absolute);
client.DownloadStringCompleted += (s, e) =>
{
MainPage mainpage = new MainPage();
mainpage.RefreshButton();
if (e.Error == null)
{
RootObject result = JsonConvert.DeserializeObject<RootObject>(e.Result);
hereRestProperty = new ObservableCollection<Item>(result.results.items);
for (int i = 0; i < hereRestProperty.Count; i++)
{
ardisplayLocation.Add(new CityPlace()
{
GeoLocation = new GeoCoordinate(hereRestProperty[i].coordinate.Latitude,hereRestProperty[i].coordinate.Longitude),
Content = hereRestProperty[i].title,
Description = hereRestProperty[i].vicinity
});
}
}
else
{
isFailed = Visibility.Visible;
myMessage = "Failed to load web server data, please refresh";
}
isBusy = false;
};
client.DownloadStringAsync(uri);
}
catch (Exception)
{
isBusy = false;
isFailed = Visibility.Visible;
myMessage = "Something wrong happen, please refresh";
}
}
and here is my ArDisplay.xaml.cs
private MainViewModel mvm { get { return this.DataContext as MainViewModel; } }
public ArDisplay()
{
InitializeComponent();
DataContext = App.ViewModel;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
ardisplay.StartServices();
ardisplay.ARItems = mvm.ardisplayLocation;
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
ardisplay.StopServices();
base.OnNavigatedFrom(e);
}
and my xaml
<gart:ARDisplay Name="ardisplay" AttitudeRefreshRate="50" MovementThreshold="10">
<gart:VideoPreview x:Name="videoPreview" Canvas.ZIndex="1"/>
<gart:WorldView x:Name="worldView" Canvas.ZIndex="1" ItemTemplate="{StaticResource CityItemTemplate}" MinItemScale="0.1" MaxItemScale="1.0" FarClippingPlane="300.0" NearClippingPlane="1.0"/>
<gart:HeadingIndicator x:Name="headingIndicator" Canvas.ZIndex="1" Width="300" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</gart:ARDisplay>
and my data template
<DataTemplate x:Key="CityItemTemplate">
<Border BorderBrush="Black" BorderThickness="4" CornerRadius="8" Background="#FF003847" Width="320">
<StackPanel Margin="4">
<TextBlock x:Name="NameBlock" TextWrapping="NoWrap" Text="{Binding Content}" FontSize="38" VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="1" TextTrimming="WordEllipsis"/>
<TextBlock x:Name="DescriptionBlock" TextWrapping="Wrap" Text="{Binding Description}" FontSize="24" VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="1" TextTrimming="WordEllipsis" MaxHeight="168"/>
</StackPanel>
</Border>
</DataTemplate>
Canvas.ZIndex was missing from your DataTemplate
<DataTemplate x:Key="CityItemTemplate">
<Border BorderBrush="Black" BorderThickness="4" CornerRadius="8" Background="#FF003847" Width="320" Canvas.ZIndex="2">
<StackPanel Margin="4">
<TextBlock x:Name="NameBlock" TextWrapping="NoWrap" Text="{Binding Content}" FontSize="38" VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="1" TextTrimming="WordEllipsis"/>
<TextBlock x:Name="DescriptionBlock" TextWrapping="Wrap" Text="{Binding Description}" FontSize="24" VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="1" TextTrimming="WordEllipsis" MaxHeight="168"/>
</StackPanel>
</Border>
</DataTemplate>

Render Canvas with Background to Bitmap fails to display background C#/WPF

I am writing an app in C#4.0/WPF that displays a piece of machinery in an image control within a canvas. The user drags coloured text blocks on to the image to indicate an area of wear or damage (I have written code that creates and moves a new text block as a user drags it). Once the user has completed dragging the text blocks on the canvas my aim is to capture background and text blocks in a bitmap to be sent to a report. The trouble is that when I try and do this the image that is captured shows only the text blocks that have been dragged and not the background. I have created a test project with what looks like exactly the same code to me and it captures the canvas with all contents perfectly. I have not included the code that performs the text block dragging in the test project as I dont think it is relevant, however I have included it here for clarity. I have included the relevant code from both the main and test app. I have also included images produced in both the main and test apps. I am not too hot on XAML so I suspect this is where the fault lies. I have tried to post images from both test and main apps but the site will not allow as I have less than 10 rep points! I can of course do this once I have the points.I have not included all XAML for the form as it is a big form and there is a lot of code - I can do this if required though.I have researched high and low and have posted this on other forums with no luck. Hope some one can help!
Many Thanks, Jeff
and apologies for the long posting!
Test App XAML:
<Canvas Name="canvBlade1Image" Margin="33,23,719,6">
<TextBlock Height="15" Name="tblkRed" Width="18" FontSize="11" Background="Red" Canvas.Left="200" Canvas.Top="444" HorizontalAlignment="Center" TextAlignment="Center" />
<TextBlock Background="Orange" FontSize="11" Height="15" Name="tblkOrange" TextAlignment="Center" Width="18" Canvas.Left="200" Canvas.Top="465" />
<TextBlock Background="Yellow" FontSize="11" Height="15" Name="tblkYellow" TextAlignment="Center" Width="18" Canvas.Left="200" Canvas.Top="486" />
<TextBlock Background="Lime" FontSize="11" Height="15" Name="tblkGreen" TextAlignment="Center" Width="18" Canvas.Left="200" Canvas.Top="508" />
<TextBlock Background="DodgerBlue" FontSize="11" Height="15" Name="tblkGre" TextAlignment="Center" Width="18" Canvas.Left="200" Canvas.Top="528" />
<Canvas.Background>
<ImageBrush ImageSource="C:\Users\jeff\documents\blankblade.png" Stretch="Fill"></ImageBrush>
</Canvas.Background>
test app C#
private void btnRendertoBitmap_Click(object sender, RoutedEventArgs e)
{
Transform transform = canvBlade1Image.LayoutTransform;
canvBlade1Image.LayoutTransform = null;
Size size = new Size(canvBlade1Image.ActualWidth, canvBlade1Image.ActualHeight);
canvBlade1Image.Measure(size);
canvBlade1Image.Arrange(new Rect(size));
RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96d, 96d, PixelFormats.Pbgra32);
renderBitmap.Render(canvBlade1Image);
using (FileStream outStream = new FileStream(#"C:\Users\jeff\documents\blade.png.", FileMode.Create))
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
encoder.Save(outStream);
}
}
![test app output][1]
Main app XAML
<Canvas Name="canvBlade1Image" Margin="33,23,719,6">
<TextBlock Height="15" Name="tblkRed" Width="18" FontSize="11" Background="Red" Canvas.Left="200" Canvas.Top="444" HorizontalAlignment="Center" TextAlignment="Center" />
<TextBlock Background="Orange" FontSize="11" Height="15" Name="tblkOrange" TextAlignment="Center" Width="18" Canvas.Left="200" Canvas.Top="465" />
<TextBlock Background="Yellow" FontSize="11" Height="15" Name="tblkYellow" TextAlignment="Center" Width="18" Canvas.Left="200" Canvas.Top="486" />
<TextBlock Background="Lime" FontSize="11" Height="15" Name="tblkGreen" TextAlignment="Center" Width="18" Canvas.Left="200" Canvas.Top="508" />
<TextBlock Background="DodgerBlue" FontSize="11" Height="15" Name="tblkGre" TextAlignment="Center" Width="18" Canvas.Left="200" Canvas.Top="528" />
<Canvas.Background>
<ImageBrush ImageSource="C:\Users\jeff\documents\blankblade.png" Stretch="Fill"></ImageBrush>
</Canvas.Background>
Main App C#
public bool ExportToPNG()
{
try
{
Transform transform = canvBlade1Image.LayoutTransform;
canvBlade1Image.LayoutTransform = null;
Size size = new Size(canvBlade1Image.ActualWidth, canvBlade1Image.ActualHeight);
canvBlade1Image.Measure(size);
canvBlade1Image.Arrange(new Rect(size));
RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96d, 96d, PixelFormats.Pbgra32);
renderBitmap.Render(canvBlade1Image);
using (FileStream outStream = new FileStream(#"C:\Users\jeff\documents\blade.png.", FileMode.Create))
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
encoder.Save(outStream);
}
return true;
}
catch (Exception E)
{
MessageBox.Show("Error converting blade image" + E);
return false;
}
}
![Main app output][2]
Code to drag textblocks
private void canvBlade1Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if(doNotAllowNewFault == true && (((System.Windows.Controls.TextBlock)(e.Source)).Text == ""))
{
return;
}
if (e.Source != canvBlade1Image && e.Source.GetType() == typeof(System.Windows.Controls.TextBlock))
{
overlayTextBlockBackgroundBrush = ((System.Windows.Controls.TextBlock)(e.Source)).Background;
overlayTextBlockTextAlignment = TextAlignment.Center;
overlayTextBlockFontSize = ((System.Windows.Controls.TextBlock)(e.Source)).FontSize;
overlayTextBlockText = ((System.Windows.Controls.TextBlock)(e.Source)).Text;
canvBlade1Image.Children.Remove(sourceTextBlock);
if(((System.Windows.Controls.TextBlock)(e.Source)).Text !="")
{
moveFault = true;
}
sourceElementLeft = Canvas.GetLeft((TextBlock)e.Source);
sourceElementTop = Canvas.GetLeft((TextBlock)e.Source);
canvBlade1Image.Children.Remove(sourceTextBlock);
sourceTextBlock = (TextBlock)e.Source;
mIsDown = true;
mStartPoint = e.GetPosition(canvBlade1Image);
e.Handled = true;
}
}
private void canvBlade1Image_MouseMove(object sender, MouseEventArgs e)
{
Double actualX = Math.Abs(e.GetPosition(canvBlade1Image).X);
Double startX = mStartPoint.X;
Double actualY = Math.Abs(e.GetPosition(canvBlade1Image).Y);
Double startY = mStartPoint.Y;
if (mIsDown)
{
if (!mIsDragging)
{
DragStarted();
}
}
if (mIsDragging)
{
DragMoved();
}
e.Handled = true;
}
private void DragStarted()
{
mIsDragging = true;
sourceElementLeft = Canvas.GetLeft(sourceTextBlock);
sourceElementTop = Canvas.GetTop(sourceTextBlock);
overlayTextBlock = new TextBlock();
overlayTextBlock.Background = sourceTextBlock.Background;
overlayTextBlock.Width = sourceTextBlock.RenderSize.Width;
overlayTextBlock.Height = sourceTextBlock.RenderSize.Height;
if (moveFault == false)
{
overlayTextBlock.Text = blade1FaultNumber.ToString();
}
else
{
overlayTextBlock.Text =overlayTextBlockText;
}
overlayTextBlock.FontSize = overlayTextBlockFontSize;
overlayTextBlock.TextAlignment = overlayTextBlockTextAlignment;
canvBlade1Image.Children.Add(overlayTextBlock);
overlayTextBlock.Opacity = 1;
}
private void DragMoved()
{
Point currentPosition = Mouse.GetPosition(canvBlade1Image);
double elementLeft = (currentPosition.X - mStartPoint.X) + sourceElementLeft;
double elementTop = (currentPosition.Y - mStartPoint.Y) + sourceElementTop;
Canvas.SetLeft(overlayTextBlock, elementLeft);
Canvas.SetTop(overlayTextBlock, elementTop);
}
private void canvBlade1Image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (mIsDown)
{
DragFinished(false);
e.Handled = true;
}
}
private void DragFinished(Boolean cancelled)
{
if (mIsDragging)
{
if( moveFault)
{
canvBlade1Image.Children.Remove(sourceTextBlock);
}
else
{
Canvas.SetLeft(sourceTextBlock, Canvas.GetLeft(overlayTextBlock));
Canvas.SetTop(sourceTextBlock, Canvas.GetTop(overlayTextBlock));
TextBlock replacementTextBlock = new TextBlock();
Canvas.SetLeft(replacementTextBlock, sourceElementLeft);
Canvas.SetTop(replacementTextBlock, sourceElementTop);
replacementTextBlock.Height = overlayTextBlock.Height;
replacementTextBlock.Width = overlayTextBlock.Width;
replacementTextBlock.Opacity = 1;
replacementTextBlock.FontSize = 11;
replacementTextBlock.Background = overlayTextBlock.Background;
blade1FaultNumber++;
canvBlade1Image.Children.Add(replacementTextBlock);
}
moveFault = false;
overlayTextBlock = null;
mIsDragging = false;
mIsDown = false;
txtFaultBriefDesciption1.IsEnabled = true;
txtFaultdetails1.IsEnabled = true;
cboMeters1.IsEnabled = true;
}
}
How about you add this to your canvas and get rid of the background?:
Canvas Name="canvBlade1Image" Margin="33,23,719,6">
...other stuff on canvas
Image Source="C:\Users\jeff\documents\blankblade.png"/>
/Canvas>
After much frustration I have now been able to resolve this issue. I was unable to find out why the background was not rendering in my prod app with exactly the same code as my successful test app. I therefore tried a new route of explicitly rendering the background. This worked, code below:
try
{
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
(int)canvBlade1Image.ActualWidth,(int)canvBlade1Image.ActualHeight,96d,
96d, PixelFormats.Pbgra32);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
drawingContext.DrawRectangle(canvBlade1Image.Background, null, new Rect(0, 0, canvBlade1Image.ActualWidth, canvBlade1Image.ActualHeight));
renderBitmap.Render(drawingVisual);
renderBitmap.Render(canvBlade1Image);
using (FileStream outStream = new FileStream(#"C:\Images\Keep\img1.png.", FileMode.Create))
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
encoder.Save(outStream);
}
}

Categories