This commit is contained in:
youngq 2024-12-09 16:40:12 +08:00
parent ad8f3983c9
commit 0736345acd
44 changed files with 1172 additions and 212 deletions

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace Demo.Common.Converters
{
public class AspectRatioConverter : IMultiValueConverter
{
private readonly double ratio = 1.777;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2 && values[0] is double height && values[1] is double maxWidth)
{
Console.WriteLine($@"height {height} maxWidth {maxWidth}");
var controlWidth = height * ratio;
return controlWidth > maxWidth ? maxWidth : controlWidth;
}
//else if (values.Length == 4 && values[0] is double stackHeight
// && values[1] is double stackWidth && values[2] is int userPageSize
// && values[3] is Thickness margin)
//{
// var controlWidth = stackHeight * ratio;
//}
return 0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace Demo.Common.Converters
{
public class BooleanInverseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool boolValue ? !boolValue : false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool boolValue ? !boolValue : false;
}
}
}

View File

@ -5,6 +5,22 @@
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
<Platforms>AnyCPU;x64</Platforms>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="agora_rtc_sdk" Version="4.2.1" />
<PackageReference Include="HandyControl" Version="3.5.1" />
<PackageReference Include="Masuit.Tools.Core" Version="2024.6.0" />
<PackageReference Include="Prism.DryIoc" Version="8.1.97" />
<PackageReference Include="RestSharp" Version="112.1.0" />
</ItemGroup>
<ItemGroup>
<None Update="AgoraRtcWrapper.lib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common.Dtos
{
public class BaseApiResult<T>
{
public int code { get; set; }
public string message { get; set; }
public T Data { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common.Dtos
{
public class LoginApiResponse
{
public string token { get; set; }
public string refresh_token { get; set; }
public string userName { get; set; }
public int expire { get; set; }
public string uid { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using Agora.Rtc;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common.Events
{
public class AudioVolumeIndicationEvent : PubSubEvent<AudioVolumeInfo>
{
}
}

View File

@ -0,0 +1,14 @@
using Demo.Common.Models;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common.Events
{
public class UserJoinEvent : PubSubEvent<User>
{
}
}

View File

@ -0,0 +1,14 @@
using Demo.Common.Models;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common.Events
{
public class UserLeaveEvent : PubSubEvent<User>
{
}
}

View File

@ -0,0 +1,33 @@
using Demo.Common.Dtos;
using Newtonsoft.Json.Linq;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using DataFormat = RestSharp.DataFormat;
namespace Demo.Common.Extensions
{
public static class RestClientExtension
{
public static async Task<TResult> PostAsync<TResult, TBody>(this RestClient client, TBody body) where TResult : class where TBody : class
{
RestRequest request = new RestRequest();
request.Method = Method.Post;
request.RequestFormat = DataFormat.Json;
request.AddOrUpdateHeader("Authorization", $"Bearer {Application.Current.Properties["token"]}");
request.AddJsonBody(body);
var result = await client.PostAsync<BaseApiResult<TResult>>(request);
if (result.code != 200)
{
return result.Data;
}
return null;
}
}
}

View File

@ -0,0 +1,123 @@
using Agora.Rtc;
using Masuit.Tools.Systems;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common.Helpers
{
public class AgoraHelper : IRtcEngineEventHandler
{
private static uint _localUId;
public static IRtcEngine _RtcEngineInstance;
public static int Init(string appId, uint localUid = 0)
{
if (null == _RtcEngineInstance)
{
_RtcEngineInstance = RtcEngine.CreateAgoraRtcEngine();
}
RtcEngineContext rtc_engine_ctx = new RtcEngineContext();
rtc_engine_ctx.appId = appId;
var ret = _RtcEngineInstance.Initialize(rtc_engine_ctx);
ret = _RtcEngineInstance.InitEventHandler(new RtcEngineEventHandler());
_localUId = localUid;
return ret;
}
public static int UnInit()
{
if (null != _RtcEngineInstance)
{
// Dispose engine
_RtcEngineInstance.Dispose();
_RtcEngineInstance = null;
}
return 0;
}
/// <summary>
/// 渲染用户视频
/// </summary>
/// <param name="uid"></param>
/// <param name="controlHandle"></param>
/// <returns></returns>
public static int SetupUserVideo(uint uid, long controlHandle)
{
if (_localUId == uid)
{
// 本地用户
return _RtcEngineInstance.SetupLocalVideo(new VideoCanvas
{
view = controlHandle,
renderMode = RENDER_MODE_TYPE.RENDER_MODE_HIDDEN,
mirrorMode = VIDEO_MIRROR_MODE_TYPE.VIDEO_MIRROR_MODE_DISABLED,
uid = uid,
setupMode = VIDEO_VIEW_SETUP_MODE.VIDEO_VIEW_SETUP_ADD
});
}
else
{
return _RtcEngineInstance.SetupRemoteVideo(new VideoCanvas
{
view = controlHandle,
renderMode = RENDER_MODE_TYPE.RENDER_MODE_HIDDEN,
mirrorMode = VIDEO_MIRROR_MODE_TYPE.VIDEO_MIRROR_MODE_DISABLED,
uid = uid,
setupMode = VIDEO_VIEW_SETUP_MODE.VIDEO_VIEW_SETUP_ADD
});
}
}
/// <summary>
/// 清除已渲染的用户视频
/// </summary>
/// <param name="uid"></param>
/// <param name="controlHandle"></param>
/// <returns></returns>
public static int ClearUserVideo(uint uid, long controlHandle)
{
if (_localUId == uid)
{
// 本地用户
// 删除用户渲染
return _RtcEngineInstance.SetupLocalVideo(new VideoCanvas
{
view = controlHandle,
uid = uid,
setupMode = VIDEO_VIEW_SETUP_MODE.VIDEO_VIEW_SETUP_REMOVE
});
}
else
{
return _RtcEngineInstance.SetupRemoteVideo(new VideoCanvas
{
view = controlHandle,
uid = uid,
setupMode = VIDEO_VIEW_SETUP_MODE.VIDEO_VIEW_SETUP_REMOVE
});
}
}
/// <summary>
/// 渲染用户视频
/// </summary>
/// <param name="uid"></param>
/// <param name="controlHandle"></param>
/// <param name="delUid"></param>
/// <returns></returns>
public static int SetupUserVideo(uint uid, long controlHandle, uint delUid = 0)
{
if (delUid > 0)
{
ClearUserVideo(delUid, controlHandle);
}
return SetupUserVideo(uid, controlHandle);
}
}
}

View File

@ -0,0 +1,58 @@
using Agora.Rtc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Prism.Ioc;
using Prism.Events;
using Demo.Common.Events;
using Masuit.Tools;
namespace Demo.Common.Helpers
{
public class RtcEngineEventHandler : IRtcEngineEventHandler
{
private readonly IEventAggregator aggregator;
public RtcEngineEventHandler()
{
aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
}
public override void OnUserJoined(RtcConnection connection, uint remoteUid, int elapsed)
{
//base.OnUserJoined(connection, remoteUid, elapsed);
Console.WriteLine($@"新用户加入: channelId:{connection.channelId} localuid: {connection.localUid} remoteuid:{remoteUid} elapsed: {elapsed}");
//var aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
aggregator.GetEvent<UserJoinEvent>().Publish(new Models.User
{
Id = remoteUid,
UserName = remoteUid.ToString(),
IsManager = true,
IsLocal = false
});
}
public override void OnUserOffline(RtcConnection connection, uint remoteUid, USER_OFFLINE_REASON_TYPE reason)
{
//var aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
aggregator.GetEvent<UserLeaveEvent>().Publish(new Models.User
{
Id = remoteUid,
UserName = remoteUid.ToString(),
IsManager = true,
IsLocal = false
});
}
public override void OnAudioVolumeIndication(RtcConnection connection, AudioVolumeInfo[] speakers, uint speakerNumber, int totalVolume)
{
//aggregator.GetEvent<AudioVolumeIndicationEvent>().Publish();
}
}
}

View File

@ -0,0 +1,48 @@
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common.Models
{
public class User : BindableBase
{
private long id;
public long Id
{
get { return id; }
set { SetProperty(ref id, value); }
}
private string userName;
public string UserName
{
get { return userName; }
set { SetProperty(ref userName, value); }
}
private bool isManager;
public bool IsManager
{
get { return isManager; }
set { SetProperty(ref isManager, value); }
}
private bool isLocal;
public bool IsLocal
{
get { return isLocal; }
set { SetProperty(ref isLocal, value); }
}
//private bool isMain;
//public bool IsMain
//{
// get { return isMain; }
// set { SetProperty(ref isMain, value); }
//}
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common.Models
{
public class UserInfo
{
public long Id { get; set; }
public string UserName { get; set; }
public bool IsManager { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Common
{
public static class TestCredentials
{
public static string Token { get; set; }
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Services.Interfaces
{
public interface IAgoraConfigService : IBaseService
{
Task<string> GetAgoraConfigAsync();
Task<string> GetRtcTokenAsync();
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Services.Interfaces
{
public interface IAgoraEngineService
{
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Services.Interfaces
{
public interface IAuthService : IBaseService
{
Task<string> LoginAsync();
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Demo.Services.Interfaces
{
public interface IBaseService
{
}
}

View File

@ -0,0 +1,38 @@
using Demo.Common.Dtos;
using Demo.Services.Interfaces;
using RestSharp;
using System.Windows;
namespace Demo.Services
{
public class AgoraConfigService : BaseService, IAgoraConfigService
{
public async Task<string> GetAgoraConfigAsync()
{
RestRequest request = new RestRequest("home/agora-conf");
var result = await BaseRestClient.ExecuteAsync<BaseApiResult<string>>(request);
if (!result.IsSuccessful || result.Data == null)
{
MessageBox.Show(result.ErrorMessage);
return string.Empty;
}
return result.Data.Data;
}
public async Task<string> GetRtcTokenAsync()
{
var request = new RestRequest("room/tk/rtc?roomNum=999").AddHeader("Authorization", "Bearer " + Application.Current.Properties["token"]);
var result = await BaseRestClient.ExecuteAsync<BaseApiResult<string>>(request);
if (result == null || result.Data == null)
{
MessageBox.Show("token 获取失败");
return string.Empty;
}
//return result.Data.Data;
return result.Data.Data;
}
}
}

View File

@ -0,0 +1,20 @@
using Agora.Rtc;
using Demo.Services.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo.Services
{
public class AgoraEngineService : IAgoraEngineService
{
private IRtcEngine _rtcEngine;
public AgoraEngineService()
{
}
}
}

View File

@ -0,0 +1,29 @@
using Demo.Common;
using Demo.Common.Dtos;
using Demo.Services.Interfaces;
using RestSharp;
using System.Windows;
namespace Demo.Services
{
public class AuthService : BaseService, IAuthService
{
public async Task<string> LoginAsync()
{
var restRequest = new RestRequest("auth/login");
restRequest.AddBody(new
{
account = "2",
pwd = "c81e728d9d4c2f636f067f89cc14862c"
});
var response = await BaseRestClient.ExecutePostAsync<BaseApiResult<LoginApiResponse>>(restRequest);
if (!response.IsSuccessful || response.Data == null || response.Data.code != 200)
{
MessageBox.Show("登录失败");
return string.Empty;
}
return response.Data.Data.token;
}
}
}

View File

@ -0,0 +1,17 @@
using Demo.Common;
using Demo.Services.Interfaces;
using RestSharp;
using System.Windows;
namespace Demo.Services
{
public class BaseService : IBaseService
{
protected RestClient BaseRestClient;
public BaseService(RestClient client = null)
{
BaseRestClient = client ?? new RestClient(new Uri($@"http://192.168.2.9:5192"));
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RestSharp" Version="112.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Demo.Common\Demo.Common.csproj" />
<ProjectReference Include="..\Demo.Services.Interfaces\Demo.Services.Interfaces.csproj" />
</ItemGroup>
</Project>

View File

@ -5,42 +5,60 @@ VisualStudioVersion = 17.12.35506.116
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{13DA16E8-EB84-4F95-B120-F4CC4B0162E4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meeting.V2.Demo.Services", "Meeting.V2.Demo\Services\Meeting.V2.Demo.Services\Meeting.V2.Demo.Services.csproj", "{A4A5CAFF-287C-4833-98B6-D074132ED8BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meeting.V2.Demo.Services.Interfaces", "Meeting.V2.Demo\Services\Meeting.V2.Demo.Services.Interfaces\Meeting.V2.Demo.Services.Interfaces.csproj", "{A4FC67BC-E1AE-48A3-A312-3BBB3FB1BAEB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meeting.V2.Demo", "Meeting.V2.Demo\Meeting.V2.Demo\Meeting.V2.Demo.csproj", "{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.Common", "Demo.Common\Demo.Common.csproj", "{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.Services", "Demo.Services\Demo.Services.csproj", "{F1C3D513-854E-447B-B0C8-306963706E85}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.Services.Interfaces", "Demo.Services.Interfaces\Demo.Services.Interfaces.csproj", "{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A4A5CAFF-287C-4833-98B6-D074132ED8BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4A5CAFF-287C-4833-98B6-D074132ED8BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4A5CAFF-287C-4833-98B6-D074132ED8BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4A5CAFF-287C-4833-98B6-D074132ED8BF}.Release|Any CPU.Build.0 = Release|Any CPU
{A4FC67BC-E1AE-48A3-A312-3BBB3FB1BAEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4FC67BC-E1AE-48A3-A312-3BBB3FB1BAEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4FC67BC-E1AE-48A3-A312-3BBB3FB1BAEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4FC67BC-E1AE-48A3-A312-3BBB3FB1BAEB}.Release|Any CPU.Build.0 = Release|Any CPU
{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}.Debug|x64.ActiveCfg = Debug|x64
{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}.Debug|x64.Build.0 = Debug|x64
{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}.Release|Any CPU.Build.0 = Release|Any CPU
{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}.Release|x64.ActiveCfg = Release|x64
{ED1004D9-097A-4AB6-BA74-BD5CB40D9984}.Release|x64.Build.0 = Release|x64
{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}.Debug|x64.ActiveCfg = Debug|x64
{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}.Debug|x64.Build.0 = Debug|x64
{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}.Release|Any CPU.Build.0 = Release|Any CPU
{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}.Release|x64.ActiveCfg = Release|x64
{B8ACCBD2-1C4D-4CF5-BBBD-C0A62DB64C31}.Release|x64.Build.0 = Release|x64
{F1C3D513-854E-447B-B0C8-306963706E85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1C3D513-854E-447B-B0C8-306963706E85}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1C3D513-854E-447B-B0C8-306963706E85}.Debug|x64.ActiveCfg = Debug|x64
{F1C3D513-854E-447B-B0C8-306963706E85}.Debug|x64.Build.0 = Debug|x64
{F1C3D513-854E-447B-B0C8-306963706E85}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1C3D513-854E-447B-B0C8-306963706E85}.Release|Any CPU.Build.0 = Release|Any CPU
{F1C3D513-854E-447B-B0C8-306963706E85}.Release|x64.ActiveCfg = Release|x64
{F1C3D513-854E-447B-B0C8-306963706E85}.Release|x64.Build.0 = Release|x64
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}.Debug|x64.ActiveCfg = Debug|x64
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}.Debug|x64.Build.0 = Debug|x64
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}.Release|Any CPU.Build.0 = Release|Any CPU
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}.Release|x64.ActiveCfg = Release|x64
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A4A5CAFF-287C-4833-98B6-D074132ED8BF} = {13DA16E8-EB84-4F95-B120-F4CC4B0162E4}
{A4FC67BC-E1AE-48A3-A312-3BBB3FB1BAEB} = {13DA16E8-EB84-4F95-B120-F4CC4B0162E4}
{F1C3D513-854E-447B-B0C8-306963706E85} = {13DA16E8-EB84-4F95-B120-F4CC4B0162E4}
{AAB9554E-AF4A-4B94-BFEC-B239DC9DD2A6} = {13DA16E8-EB84-4F95-B120-F4CC4B0162E4}
EndGlobalSection
EndGlobal

View File

@ -2,6 +2,7 @@
x:Class="Meeting.V2.Demo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cnt="clr-namespace:Demo.Common.Converters;assembly=Demo.Common"
xmlns:local="clr-namespace:Meeting.V2.Demo"
xmlns:prism="http://prismlibrary.com/">
<Application.Resources>

View File

@ -1,9 +1,14 @@
using Meeting.V2.Demo.Services;
using Demo.Common.Helpers;
using Demo.Services;
using Demo.Services.Interfaces;
using Meeting.V2.Demo.Services;
using Meeting.V2.Demo.Services.Interfaces;
using Meeting.V2.Demo.ViewModels;
using Meeting.V2.Demo.Views;
using Prism.Events;
using Prism.Ioc;
using Prism.Modularity;
using System;
using System.Windows;
namespace Meeting.V2.Demo
@ -21,9 +26,12 @@ namespace Meeting.V2.Demo
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IConfigureService, ConfigureService>();
containerRegistry.RegisterSingleton<IAgoraConfigService, AgoraConfigService>();
containerRegistry.RegisterSingleton<IAuthService, AuthService>();
containerRegistry.RegisterForNavigation<VideoView, VideoViewModel>();
containerRegistry.RegisterForNavigation<VideoAreaView, VideoAreaViewModel>();
containerRegistry.RegisterSingleton<IEventAggregator, EventAggregator>();
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
@ -32,15 +40,24 @@ namespace Meeting.V2.Demo
//moduleCatalog.AddModule<ModuleNameModule>();
}
protected override void OnInitialized()
protected override async void OnInitialized()
{
base.OnInitialized();
var config = Container.Resolve<IConfigureService>();
if (config != null)
{
config.Init();
await config.Init();
}
}
protected override void OnExit(ExitEventArgs e)
{
AgoraHelper.UnInit();
Console.WriteLine("推出了!");
base.OnExit(e);
}
}
}

View File

@ -1,23 +1,39 @@
using Demo.Common;
using AngleSharp.Io;
using Demo.Common;
using Demo.Common.Helpers;
using Demo.Services.Interfaces;
using Meeting.V2.Demo.Views;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Meeting.V2.Demo.Services.Interfaces
{
public class ConfigureService : IConfigureService
{
private readonly IRegionManager _regionManager;
private readonly IAuthService _authService;
public ConfigureService(IRegionManager regionManager)
public ConfigureService(IRegionManager regionManager, IAuthService authService)
{
this._regionManager = regionManager;
this._authService = authService;
}
public void Init()
public async Task Init()
{
_regionManager.RegisterViewWithRegion<VideoAreaView>(RegionNames.VideoRegion);
var token = await _authService.LoginAsync();
if (string.IsNullOrWhiteSpace(token) )
{
MessageBox.Show("登录失败");
return;
}
System.Windows.Application.Current.Properties["token"] = token;
//var appid = await _agoraConfigService.GetAgoraConfigAsync();
}
}
}

View File

@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Meeting.V2.Demo.Services.Interfaces
{
public interface IConfigureService
{
void Init();
Task Init();
}
}

View File

@ -4,12 +4,15 @@
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>True</UseWindowsForms>
<Platforms>AnyCPU;x64</Platforms>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<ItemGroup>
<None Remove="Assets\manager.svg" />
<None Remove="Assets\more.svg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="agora_rtc_sdk" Version="4.2.1" />
<PackageReference Include="HandyControl" Version="3.5.1" />
<PackageReference Include="MahApps.Microsoft.DwayneNeed" Version="1.0.1" />
<PackageReference Include="Prism.DryIoc" Version="8.1.97" />
@ -17,8 +20,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Demo.Common\Demo.Common.csproj" />
<ProjectReference Include="..\Services\Meeting.V2.Demo.Services.Interfaces\Meeting.V2.Demo.Services.Interfaces.csproj" />
<ProjectReference Include="..\Services\Meeting.V2.Demo.Services\Meeting.V2.Demo.Services.csproj" />
<ProjectReference Include="..\..\Demo.Services.Interfaces\Demo.Services.Interfaces.csproj" />
<ProjectReference Include="..\..\Demo.Services\Demo.Services.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="Assets\manager.svg" />

View File

@ -1,19 +1,118 @@
using Prism.Mvvm;
using Agora.Rtc;
using Demo.Common.Events;
using Demo.Common.Helpers;
using Demo.Common.Models;
using Demo.Services.Interfaces;
using HandyControl.Controls;
using HandyControl.Data;
using Masuit.Tools;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using System;
using System.Windows;
namespace Meeting.V2.Demo.ViewModels
{
public class MainWindowViewModel : BindableBase
{
public MainWindowViewModel(IEventAggregator aggregator, IAgoraConfigService agoraConfigService)
{
this._aggregator = aggregator;
this._agoraConfigService = agoraConfigService;
}
private string _title = "Prism Application";
private readonly IEventAggregator _aggregator;
private readonly IAgoraConfigService _agoraConfigService;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
private long uid = (DateTime.Now.Ticks % 100000).ToString("D5").ToInt64();
public long UId
{
get { return uid; }
set { SetProperty(ref uid, value); }
}
private bool isJoin;
public bool IsJoin
{
get { return isJoin; }
set { SetProperty(ref isJoin, value); }
}
public DelegateCommand JoinChannelCommand => new DelegateCommand(JoinChannel);
async void JoinChannel()
{
AgoraHelper.Init("4a4f7be64fa1404ebda74784fe9ac381", (uint)UId);
var rtcToken = await _agoraConfigService.GetRtcTokenAsync();
if (!string.IsNullOrWhiteSpace(rtcToken))
Application.Current.Properties["rtctoken"] = rtcToken;
var result = AgoraHelper._RtcEngineInstance.EnableVideo();
Console.WriteLine($@"{nameof(JoinChannel)}-EnableVideo : {result}");
result = AgoraHelper._RtcEngineInstance.StartPreview(VIDEO_SOURCE_TYPE.VIDEO_SOURCE_CAMERA_PRIMARY);
Console.WriteLine($@"{nameof(JoinChannel)}-StartPreview : {result}");
ChannelMediaOptions options = new ChannelMediaOptions();
options.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING);
options.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
// 发布麦克风采集的音频流
options.publishMicrophoneTrack.SetValue(true);
// 发布摄像头采集的视频流
options.publishCameraTrack.SetValue(true);
// 自动订阅所有音频流
options.autoSubscribeAudio.SetValue(true);
// 自动订阅所有视频流
options.autoSubscribeVideo.SetValue(true);
AgoraHelper._RtcEngineInstance.JoinChannel(System.Windows.Application.Current.Properties["rtctoken"].ToString(), "999", (uint)UId, options);
///启用用户音量提示。
AgoraHelper._RtcEngineInstance.EnableAudioVolumeIndication(200, 5, false);
_aggregator.GetEvent<UserJoinEvent>().Publish(new User
{
Id = UId,
UserName = UId.ToString(),
IsManager = true,
IsLocal = true
});
IsJoin = !IsJoin;
Growl.Success("加入频道成功");
}
public DelegateCommand LeavelChannelCommand => new DelegateCommand(LeavelChannel);
void LeavelChannel()
{
AgoraHelper._RtcEngineInstance.LeaveChannel();
AgoraHelper._RtcEngineInstance.StopPreview();
AgoraHelper._RtcEngineInstance.DisableVideo();
_aggregator.GetEvent<UserLeaveEvent>().Publish(new User
{
Id = UId,
UserName = UId.ToString(),
IsManager = true,
IsLocal = true
});
IsJoin = !IsJoin;
Growl.Success("离开频道成功");
}
}
}
}

View File

@ -1,77 +1,140 @@
using Demo.Common.Models;
using Demo.Common.Events;
using Meeting.V2.Demo.Core.Mvvm;
using Prism.Commands;
using Prism.Events;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using User = Demo.Common.Models.User;
namespace Meeting.V2.Demo.ViewModels
{
public class VideoAreaViewModel : RegionViewModelBase
{
public VideoAreaViewModel(IRegionManager regionManager) : base(regionManager)
public VideoAreaViewModel(IRegionManager regionManager,
IEventAggregator aggregator) : base(regionManager)
{
UserInfos.Add(new UserInfo() { Id = 1, UserName = "张三", IsManager = true });
UserInfos.Add(new UserInfo() { Id = 2, UserName = "李四", IsManager = false });
this.aggregator = aggregator;
UinfoProp = new UserInfo
aggregator.GetEvent<UserJoinEvent>().Subscribe((a) => Application.Current.Dispatcher.BeginInvoke(() =>
{
Id = 111,
UserName = "王五",
IsManager = false
};
UserInfos.Add(a);
}));
aggregator.GetEvent<UserLeaveEvent>().Subscribe((a) =>
{
Application.Current.Dispatcher.BeginInvoke(() =>
{
if (a.IsLocal)
{
UserInfos.Clear();
FocusUser = null;
return;
}
for (int i = 0; i < UserInfos.Count; i++)
{
if (UserInfos[i].Id == a.Id)
{
UserInfos.RemoveAt(i);
return;
}
}
});
});
//UserInfos.Add(new UserInfo() { Id = 1, UserName = "张三", IsManager = true });
//UserInfos.Add(new UserInfo() { Id = 2, UserName = "李四", IsManager = false });
//UserInfos.Add(new UserInfo() { Id = 3, UserName = "李四", IsManager = false });
//UserInfos.Add(new UserInfo() { Id = 4, UserName = "李四", IsManager = false });
//UserInfos.Add(new UserInfo() { Id = 4, UserName = "李四", IsManager = false });
//UserInfos.Add(new UserInfo() { Id = 4, UserName = "李四", IsManager = false });
}
private ObservableCollection<UserInfo> userInfos = new();
private ObservableCollection<User> userInfos = new();
/// <summary>
/// 用户列表
/// </summary>
public ObservableCollection<UserInfo> UserInfos
public ObservableCollection<User> UserInfos
{
get { return userInfos; }
set { SetProperty(ref userInfos, value); }
}
private string fieldName = "卧槽";
public string PropertyNameAA
private int userPageSize = 6;
/// <summary>
/// 每页用户数
/// </summary>
public int UserPageSize
{
get { return fieldName; }
set { SetProperty(ref fieldName, value); }
get { return userPageSize; }
set { SetProperty(ref userPageSize, value); }
}
private UserInfo Uinfo;
public UserInfo UinfoProp
private User focusUser;
private readonly IEventAggregator aggregator;
/// <summary>
/// 焦点用户
/// </summary>
public User FocusUser
{
get { return Uinfo; }
set { SetProperty(ref Uinfo, value); }
get { return focusUser; }
set { SetProperty(ref focusUser, value); }
}
public override void OnNavigatedTo(NavigationContext navigationContext)
/// <summary>
/// 选择焦点用户
/// </summary>
public DelegateCommand<User> SelectedUserCommand => new DelegateCommand<User>((u) =>
{
//base.OnNavigatedTo(navigationContext);
Init();
}
private void Init()
if (FocusUser != u || FocusUser.Id != u.Id)
{
UserInfos.Add(new UserInfo() { Id = 3, UserName = "王五", IsManager = true });
UserInfos.Add(new UserInfo() { Id = 4, UserName = "李2", IsManager = false });
FocusUser = u;
}
});
///// <summary>
///// 选择焦点用户
///// </summary>
//public DelegateCommand SelectedUserCommand => new DelegateCommand(() =>
//{
// Console.WriteLine("2222222222222222222222222222");
//});
public DelegateCommand TestCommand => new DelegateCommand(ExecuteCommandName);
void ExecuteCommandName()
/// <summary>
/// 用户列表尺寸大小改变事件
/// </summary>
public DelegateCommand<object> UserStackPanelSizeChangeCommand => new DelegateCommand<object>((e) =>
{
MessageBox.Show($@"{UserInfos.Count} Id{string.Join(',', UserInfos.Select(x => x.Id))}");
if (e is StackPanel control)
{
Console.WriteLine($@"hei:{control.ActualHeight} widht:{control.ActualWidth}");
var width = ((control.ActualHeight - 6) * 1.777);
UserPageSize = (int)Math.Floor(control.ActualWidth / width);
if (UserPageSize < UserInfos.Count && UserPageSize > 0)
{
while (UserInfos.Count != UserPageSize)
{
UserInfos.RemoveAt(UserInfos.Count - 1);
}
}
}
});
}
}

View File

@ -1,39 +1,93 @@
using Demo.Common.Models;
using Agora.Rtc;
using Demo.Common.Events;
using Demo.Common.Helpers;
using Demo.Common.Models;
using Masuit.Tools;
using Meeting.V2.Demo.Core.Mvvm;
using Meeting.V2.Demo.Views;
using Prism.Commands;
using Prism.Regions;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
namespace Meeting.V2.Demo.ViewModels
{
public class VideoViewModel : RegionViewModelBase
public class VideoViewModel : ViewModelBase
{
public VideoViewModel(IRegionManager regionManager) : base(regionManager)
private VideoView _view;
public VideoViewModel()
{
//aggregator.GetEvent<UserJoinEvent>().Subscribe(RenderVideoView);
}
private UserInfo userInfo;
public DelegateCommand<VideoView> LoadedCommand => new DelegateCommand<VideoView>((u) =>
{
if (u == null)
return;
Console.WriteLine($@"LoadedCoommand 被执行了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
this._view = u;
RenderVideoView();
});
/// <summary>
/// 上一次渲染的用户信息
/// </summary>
private User oldUserInfo;
private User userInfo;
/// <summary>
/// 用户信息
/// </summary>
public UserInfo UserInfo
public User UserInfo
{
get { return userInfo; }
set { SetProperty(ref userInfo, value); }
set
{
oldUserInfo = userInfo.DeepClone();
SetProperty(ref userInfo, value);
RenderVideoView();
}
}
public DelegateCommand OperClickCommand => new DelegateCommand(() =>
{
MessageBox.Show($@"id:{userInfo.Id} userName:{userInfo.UserName} isManager:{userInfo.IsManager}");
Console.WriteLine($@"id:{UserInfo.Id} userName:{UserInfo.UserName} isManager:{UserInfo.IsManager}");
UserInfo.IsManager = !UserInfo.IsManager;
userInfo.UserName = DateTime.Now.ToString();
Console.WriteLine($@"id:{UserInfo.Id} userName:{UserInfo.UserName} isManager:{UserInfo.IsManager}");
//MessageBox.Show($@"id:{Id} userName:{UserName} isManager:{IsManager}");
});
public void RenderVideoView()
{
if (_view == null || UserInfo == null || UserInfo.Id <= 0)
{
if (_view != null)
{
//_view.pic_frame.Invoke(() =>
//{
// _view.pic_frame.Refresh();
//});
if (oldUserInfo != null)
{
AgoraHelper.ClearUserVideo((uint)(oldUserInfo.Id), (long)_view.pic_frame.Handle);
_view.pic_frame.Invoke(() =>
{
_view.pic_frame.Refresh();
});
}
}
return;
}
AgoraHelper.SetupUserVideo((uint)UserInfo.Id, (long)_view.pic_frame.Handle, (uint)(oldUserInfo == null ? 0 : oldUserInfo.Id));
}
}
}

View File

@ -3,14 +3,17 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cmn="clr-namespace:Demo.Common;assembly=Demo.Common"
xmlns:cnt="clr-namespace:Demo.Common.Converters;assembly=Demo.Common"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:intertop="clr-namespace:Microsoft.DwayneNeed.Interop;assembly=MahApps.Microsoft.DwayneNeed"
xmlns:local="clr-namespace:Meeting.V2.Demo.Views"
xmlns:prism="http://prismlibrary.com/"
xmlns:wfc="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:wfh="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
Width="960"
Height="640"
Width="962"
Height="642"
MinWidth="1050"
MinHeight="642"
prism:ViewModelLocator.AutoWireViewModel="True"
Background="#252525"
CloseButtonForeground="Red"
@ -18,16 +21,55 @@
NonClientAreaForeground="White"
OtherButtonForeground="White"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<cnt:BooleanInverseConverter x:Key="BoolInverseConverter" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="55" />
</Grid.RowDefinitions>
<ContentControl prism:RegionManager.RegionName="{x:Static cmn:RegionNames.VideoRegion}" />
<DockPanel Grid.Row="1" Background="#242424">
<Button Height="45">加入频道</Button>
</DockPanel>
<ContentControl Grid.Row="0" prism:RegionManager.RegionName="{x:Static cmn:RegionNames.VideoRegion}" />
<ScrollViewer
Grid.RowSpan="2"
HorizontalAlignment="Right"
VerticalScrollBarVisibility="Hidden">
<StackPanel Margin="0,10,10,10" VerticalAlignment="Top" />
</ScrollViewer>
<StackPanel
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="#242424"
Orientation="Horizontal">
<hc:TextBox
x:Name="uid"
HorizontalAlignment="Center"
VerticalAlignment="Center"
hc:InfoElement.Placeholder="输入任意UID"
IsEnabled="{Binding IsJoin, Converter={StaticResource BoolInverseConverter}}"
Text="{Binding UId}" />
<Button
Height="45"
Margin="10"
Command="{Binding JoinChannelCommand}"
IsEnabled="{Binding IsJoin, Converter={StaticResource BoolInverseConverter}}">
加入频道
</Button>
<Button
Height="45"
Margin="10"
Command="{Binding LeavelChannelCommand}"
IsEnabled="{Binding IsJoin}">
离开频道
</Button>
</StackPanel>
<!--<local:VideoView x:Name="videoVIew" />-->
</Grid>
</hc:Window>

View File

@ -2,38 +2,86 @@
x:Class="Meeting.V2.Demo.Views.VideoAreaView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cnt="clr-namespace:Demo.Common.Converters;assembly=Demo.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:Meeting.V2.Demo.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
x:Name="root"
d:DesignHeight="530"
d:DesignWidth="960"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<UserControl.Resources>
<cnt:AspectRatioConverter x:Key="AspectRatioConverter" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" MaxHeight="155" />
<RowDefinition Height="5*" />
</Grid.RowDefinitions>
<ItemsControl Padding="10" ItemsSource="{Binding UserInfos}">
<ItemsControl
x:Name="user_vid"
Grid.Row="0"
Padding="3"
ItemsSource="{Binding UserInfos}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
x:Name="stack_users"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal" />
Background="#2D3033"
Orientation="Horizontal">
<!--<i:Interaction.Triggers>
<i:EventTrigger EventName="SizeChanged">
<i:InvokeCommandAction Command="{Binding UserStackPanelSizeChangeCommand}" CommandParameter="{Binding ElementName=stack_users}" />
</i:EventTrigger>
</i:Interaction.Triggers>-->
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:VideoView UserName="{Binding UserName}" />
<local:VideoView
Margin="3"
Command="{Binding DataContext.SelectedUserCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}"
UserInfo="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}">
<local:VideoView.Width>
<MultiBinding Converter="{StaticResource AspectRatioConverter}">
<Binding ElementName="user_vid" Path="ActualHeight" />
<Binding ElementName="user_vid" Path="ActualWidth" />
<!--<Binding Path="DataContext.UserPageSize" RelativeSource="{RelativeSource AncestorType=UserControl}" />
<Binding Path="Margin" RelativeSource="{RelativeSource Mode=Self}" />-->
</MultiBinding>
</local:VideoView.Width>
</local:VideoView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--<StackPanel Name="stack_user" Orientation="Horizontal" />-->
<local:VideoView x:Name="main_video" Grid.Row="1" />
<Border
x:Name="border_main"
Grid.Row="1"
Grid.RowSpan="2"
Background="#2D3033">
<local:VideoView x:Name="main_video" UserInfo="{Binding DataContext.FocusUser, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}}">
<!--<local:VideoView.Width>
<MultiBinding Converter="{StaticResource AspectRatioConverter}">
<Binding ElementName="border_main" Path="ActualHeight" />
<Binding ElementName="border_main" Path="ActualWidth" />
</MultiBinding>
</local:VideoView.Width>-->
</local:VideoView>
</Border>
<Button Command="{Binding TestCommand}" Content="测试按钮" />
</Grid>
</UserControl>

View File

@ -24,5 +24,7 @@ namespace Meeting.V2.Demo.Views
{
InitializeComponent();
}
}
}

View File

@ -5,6 +5,7 @@
xmlns:cnt="clr-namespace:Demo.Common.Converters;assembly=Demo.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:intertop="clr-namespace:Microsoft.DwayneNeed.Interop;assembly=MahApps.Microsoft.DwayneNeed"
xmlns:local="clr-namespace:Meeting.V2.Demo.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -12,14 +13,22 @@
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:wfc="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:wfh="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
MinWidth="160"
MinHeight="90"
d:DesignHeight="90"
d:DesignWidth="160"
x:Name="root"
MinWidth="135"
MinHeight="75"
d:DesignHeight="1080"
d:DesignWidth="1920"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<prism:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<UserControl.Resources>
<cnt:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<cnt:AspectRatioConverter x:Key="AspectRatioConverter" />
</UserControl.Resources>
<Grid>
@ -31,9 +40,10 @@
<WindowsFormsHost x:Name="gridHost">
<wfc:PictureBox
x:Name="pic_frame"
BackColor="#2d3033"
BackColor="#000000"
Dock="Fill"
MouseEnter="pic_frame_MouseEnter"
MouseDoubleClick="pic_frame_MouseDoubleClick"
MouseLeave="pic_frame_MouseLeave"
SizeMode="StretchImage" />
</WindowsFormsHost>
@ -61,7 +71,7 @@
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Orientation="Horizontal">
<Border Background="#ff801a" Visibility="{Binding UserInfo.IsManager, Converter={StaticResource BoolToVisibilityConverter}}">
<Border Background="#ff801a" Visibility="{Binding UserInfo.IsManager, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}">
<svgc:SvgViewbox
x:Name="icon_manager"
Margin="3"
@ -76,7 +86,7 @@
VerticalAlignment="Center"
FontSize="12"
Foreground="White"
Text="{Binding UserInfo.UserName}" />
Text="{Binding UserInfo.UserName, Mode=TwoWay}" />
</Border>
</StackPanel>
<!--#endregion-->

View File

@ -1,20 +1,11 @@
using Demo.Common.Models;
using Meeting.V2.Demo.ViewModels;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Meeting.V2.Demo.Views
{
@ -23,87 +14,41 @@ namespace Meeting.V2.Demo.Views
/// </summary>
public partial class VideoView : UserControl
{
public VideoView()
{
InitializeComponent();
pic_frame.ImageLocation = $@"E:\桌面\Wall\wallhaven-j3m8v5.jpg";
}
//public UserInfo UserInfo
//{
// get { return (UserInfo)GetValue(UserInfoProperty); }
// set { SetValue(UserInfoProperty, value); }
//}
//// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
//public static readonly DependencyProperty UserInfoProperty =
// DependencyProperty.Register("UserInfo", typeof(UserInfo), typeof(VideoView), new FrameworkPropertyMetadata(null));
public string UserName
public User UserInfo
{
get { return (string)GetValue(UserNameProperty); }
set {
SetValue(UserNameProperty, value); }
get { return (User)GetValue(UserInfoProperty); }
set { SetValue(UserInfoProperty, value); }
}
// Using a DependencyProperty as the backing store for UserName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UserNameProperty =
DependencyProperty.Register("UserName", typeof(string), typeof(VideoView), new PropertyMetadata("未知名称", new PropertyChangedCallback(OnUserNamePropertyChangedCallback)));
private static void OnUserNamePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UserInfoProperty =
DependencyProperty.Register("UserInfo", typeof(User), typeof(VideoView), new PropertyMetadata(null, new(OnUserInfoPropertyChangedCallback)));
private static void OnUserInfoPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is VideoView videoView )
{
videoView.txt_uname.Text = (string)e.NewValue;
var viewDataContext = videoView.DataContext as VideoViewModel;
viewDataContext.UserInfo = e.NewValue as User;
}
}
//public bool IsManager
//{
// get { return (bool)GetValue(IsManagerProperty); }
// set { SetValue(IsManagerProperty, value); }
//}
//// Using a DependencyProperty as the backing store for IsManager. This enables animation, styling, binding, etc...
//public static readonly DependencyProperty IsManagerProperty =
// DependencyProperty.Register("IsManager", typeof(bool), typeof(VideoView), new PropertyMetadata(false, new(OnIsManagerPropertyChangedCallback)));
//private static void OnIsManagerPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
//{
// if (d is VideoView videoView)
// {
// if (e.NewValue is bool b)
// {
// var db = videoView.DataContext as VideoViewModel;
// db.UserInfo.IsManager = b;
// //if (b)
// //{
// // videoView.icon_manager.Visibility = Visibility.Visible;
// //}
// //else
// //{
// // videoView.icon_manager.Visibility = Visibility.Collapsed;
// //}
// }
// }
//}
#region
/// <summary>
/// 控制操作按钮显示
/// </summary>
private bool IsOperVisible = false;
private void pic_frame_MouseEnter(object sender, EventArgs e)
{
btn_oper.Visibility = Visibility.Visible;
@ -136,8 +81,45 @@ namespace Meeting.V2.Demo.Views
{
await Task.Delay(50).ConfigureAwait(false);
IsOperVisible = false;
}
#endregion
#region
private void ExcuteCommand()
{
// 点击操作按钮,触发命令
Command?.Execute(CommandParameter);
}
// 依赖属性定义
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(VideoView), new PropertyMetadata(null));
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(VideoView), new PropertyMetadata(null));
private void pic_frame_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
ExcuteCommand();
}
#endregion
}
}

View File

@ -1,7 +0,0 @@
namespace Meeting.V2.Demo.Services.Interfaces
{
public interface IMessageService
{
string GetMessage();
}
}

View File

@ -4,4 +4,8 @@
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RestSharp" Version="112.1.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Meeting.V2.Demo.Services.Interfaces
{
public interface IAgoraConfigService : IBaseService
{
Task GetAgoraConfig();
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Meeting.V2.Demo.Services.Interfaces
{
public interface IBaseService
{
}
}

View File

@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Demo.Common\Demo.Common.csproj" />
<ProjectReference Include="..\Meeting.V2.Demo.Services.Interfaces\Meeting.V2.Demo.Services.Interfaces.csproj" />
</ItemGroup>

View File

@ -1,12 +0,0 @@
using Meeting.V2.Demo.Services.Interfaces;
namespace Meeting.V2.Demo.Services
{
public class MessageService : IMessageService
{
public string GetMessage()
{
return "Hello from the Message Service";
}
}
}